<?php
// +----------------------------------------------------------------------
// | View 视图服务
// +----------------------------------------------------------------------
// | * 视图层
// | * 1.查询所有关联表的字段
// | * 2.处理字段（可显示、可编辑、可排序、可分组、可过滤）、还要通过临时字段、字段权限进行判断，组装出view需要的字段
// | * 3.组装view视图的表格列
// +----------------------------------------------------------------------
// | 错误编码头 231xxx
// +----------------------------------------------------------------------

namespace common\service;

use common\exception\LogicException;
use common\exception\ReviewFeedbackPermissionException;
use common\model\CosMediaModel;
use common\model\EntityEpisodeRelationModel;
use common\model\EntityModel;
use common\model\EpisodeStepCategorySealTimeModel;
use common\model\NoteEntityRelationModel;
use common\model\NoteModel;
use common\model\PlanModel;
use common\model\ProjectModel;
use common\model\RelationNoteEntityRelationModel;
use common\model\ReviewFeedbackEntityModel;
use common\model\ReviewFeedbackModel;
use common\model\StageModel;
use common\model\StatusModel;
use common\model\StepCategoryModel;
use common\model\StepModel;
use common\model\StepUpstreamModel;
use common\model\StepWorkflowModel;
use common\model\TaskModel;
use common\model\UserModel;
use support\ErrorCode;
use support\SingletonTrait;
use think\Exception;
use think\Hook;
use Throwable;
use Webman\Stomp\Client;

class ReviewFeedbackService
{
    use SingletonTrait;

    private $reviewFeedbackModel;

    public function __construct()
    {
        $this->reviewFeedbackModel = model(ReviewFeedbackModel::class);
    }

    /**
     * 创建审核反馈
     * @param $data
     * @return array|bool|null
     * @throws Throwable
     * @throws Exception
     */
    public function save($data)
    {
        $reviewFeedbackModel = model(ReviewFeedbackModel::class);
        $reviewFeedbackModel->setCurrentModuleCode('review_feedback');
        $reviewFeedbackModule = module_by_code('review_feedback');
        $currentModuleLinkRelation = $reviewFeedbackModel->parserFilterModuleRelation(true);
        $relationIds = [];

        $reviewFeedbackModel->startTrans();
        try {
            if (empty($data["review_feedback"]["id"])) {
                //新增
                foreach ($data as $moduleCode => $item) {
                    if (isset($currentModuleLinkRelation[$moduleCode]) && $currentModuleLinkRelation[$moduleCode]['module_code'] === 'cos_media') {
                        $mediaIds = [];

                        if (is_many_dimension_array($item)) {
                            foreach ($item as $mediaItem) {
                                if (empty($mediaItem)) {
                                    continue;
                                }

                                $mediaData = CosMediaService::getInstance()->findAddMedia($mediaItem);
                                if (!$mediaData) {
                                    throw new LogicException(model(CosMediaModel::class)->getError(), ErrorCode::MEDIA_ADD_FAILURE);
                                } else {
                                    if (!in_array($mediaData['id'], $mediaIds)) {
                                        $mediaIds[] = $mediaData['id'];
                                    }
                                }
                            }
                        }
                        $relationIds[$moduleCode] = join(',', $mediaIds);
                    }
                }

                foreach ($relationIds as $filed => $relationId) {
                    $data['review_feedback'][$filed] = $relationId;
                }

                $commonService = CommonService::instance("review_feedback");
                $resData = $commonService->commonCreate(['data' => $data['review_feedback']], $reviewFeedbackModule);
            } else {
                foreach ($data as $moduleCode => $item) {
                    if (isset($currentModuleLinkRelation[$moduleCode]) && $currentModuleLinkRelation[$moduleCode]['module_code'] === 'cos_media') {
                        $mediaIds = [];
                        if (is_many_dimension_array($item)) {
                            foreach ($item as $mediaItem) {
                                if (empty($mediaItem)) {
                                    continue;
                                }
                                // 判定是否需要更新
                                if (!empty($mediaItem["id"])) {
                                    $mediaIds[] = $mediaItem["id"];
                                    if (!empty($mediaItem['param'])) {
                                        CosMediaService::getInstance()->findAddMedia($mediaItem);
                                    }
                                } else {
                                    $mediaData = CosMediaService::getInstance()->findAddMedia($mediaItem);
                                    if (!in_array($mediaData['id'], $mediaIds)) {
                                        $mediaIds[] = $mediaData['id'];
                                    }
                                }
                            }
                        }
                        $relationIds[$moduleCode] = join(',', $mediaIds);
                    }
                }

                // 把水平自定义关联数据回插入主表
                foreach ($relationIds as $filed => $relationId) {
                    $data['review_feedback'][$filed] = $relationId;
                }
                $commonService = CommonService::instance("review_feedback");
                $resData = $commonService->commonUpdate(['data' => $data['review_feedback']], $reviewFeedbackModule, false);
            }

            $reviewFeedbackModel->commit();
            return $resData;
        } catch (Throwable $e) {
            $reviewFeedbackModel->rollback();
            throw $e;
        }
    }

    /**
     * 获取审核反馈列表
     * @param $param
     * @return array
     */
    public function getReviewFeedbackList($param)
    {
        $reviewFeedbackModel = $this->reviewFeedbackModel;
        $page = $param["page"] ?? [1, config("default_page_total")];

        $filter = $param["filter"] ?? [];
        $filter['demand_type'] = $filter['demand_type'] ?? 'review_feedback';
        $order = $param["order"] ?? ["FIELD(status,'in_progress','not_started','final')", "created desc"];
        $reviewFeedbackList = $reviewFeedbackModel->where($filter)->page(...$page)->order($order)->select();
        if (empty($reviewFeedbackList)) {
            return [];
        }
        //创建人id
        $createdBy = [];
        $cosMediaListData = [];
        $projectIds = [];
        foreach ($reviewFeedbackList as $reviewFeedbackIndex => $reviewFeedbackItem) {
            $createdBy[] = $reviewFeedbackItem['created_by'];
            $reviewFeedbackItem["json"] = json_decode($reviewFeedbackItem["json"], true);
            $projectIds[$reviewFeedbackItem['project_id']] = $reviewFeedbackItem['project_id'];

            if (!empty($reviewFeedbackItem["json"]["review_feedback_cos_media"])) {
                $reviewFeedbackCosMedia = explode(",", $reviewFeedbackItem["json"]["review_feedback_cos_media"]);
                foreach ($reviewFeedbackCosMedia as $reviewFeedbackCosMediaItem) {
                    $cosMediaListData[$reviewFeedbackCosMediaItem] = $reviewFeedbackCosMediaItem;
                }

                $reviewFeedbackItem["review_feedback_cos_media"] = $reviewFeedbackCosMedia;
            }

            unset($reviewFeedbackItem["json"]);
            $reviewFeedbackList[$reviewFeedbackIndex] = $reviewFeedbackItem;
        }

        $projectIds = array_values($projectIds);

        $createdBy = array_unique($createdBy);

        $cosMediaDataList = [];
        if (!empty($cosMediaListData)) {
            $cosMediaList = model(CosMediaModel::class)->where(['id' => ["IN", join(",", $cosMediaListData)]])->select();
            foreach ($cosMediaList as $cosMediaItem) {
                $cosMediaItem['param'] = json_decode($cosMediaItem['param'], true);
                $cosMediaDataList[$cosMediaItem["id"]] = $cosMediaItem;
            }
        }
        $projectMap = [];
        if (!empty($projectIds)) {
            $projectList = model(ProjectModel::class)->where(['id' => ["IN", $projectIds]])->field("id,name")->select();
            $projectMap = array_column($projectList, null, 'id');
        }

        //查询创建人
        $createdByMap = [];
        if (!empty($createdBy)) {
            $createdData = model(UserModel::class)->where(["id" => ["IN", $createdBy]])->field("id,name")->select();
            $createdByMap = array_column($createdData, null, "id");
        }

        return array_map(function ($reviewFeedbackItem) use ($createdByMap, $cosMediaDataList, $projectMap) {
            $reviewFeedbackItem["cos_media"] = [];
            if (!empty($reviewFeedbackItem["review_feedback_cos_media"])) {
                foreach ($reviewFeedbackItem["review_feedback_cos_media"] as $reviewFeedbackCosMedia) {
                    if (isset($cosMediaDataList[$reviewFeedbackCosMedia])) {
                        $reviewFeedbackItem["cos_media"][] = $cosMediaDataList[$reviewFeedbackCosMedia];
                    }
                }
                unset($reviewFeedbackItem["review_feedback_cos_media"]);
            }
            $reviewFeedbackItem['project_name'] = $projectMap[$reviewFeedbackItem['project_id']]['name'];
            $reviewFeedbackItem["created_by"] = $createdByMap[$reviewFeedbackItem["created_by"]] ?? [];
            return $reviewFeedbackItem;
        }, $reviewFeedbackList);
    }

    /**
     * 删除审核反馈
     * @param $filter
     * @return array
     * @throws Throwable
     */
    public function deleteFeedback($filter)
    {
        // 删除审核代办
        $reviewFeedbackModel = model(ReviewFeedbackModel::class);
        $reviewFeedbackList = $reviewFeedbackModel->where($filter)->field("id,status")->select();
        if (empty($reviewFeedbackList)) {
            return [];
        }

        //非关闭的需求单没有需求数据可以进行删除
        $reviewFeedbackIds = [];
        $existIsStarted = false;
        foreach ($reviewFeedbackList as $reviewFeedbackItem) {
            if ($reviewFeedbackItem['status'] != ReviewFeedbackModel::STATUS_NOT_STARTED) {
                $existIsStarted = true;
            }
            $reviewFeedbackIds[] = $reviewFeedbackItem['id'];
        }

        $existNote = model(NoteModel::class)->where(['review_feedback_id' => ['IN', $reviewFeedbackIds]])->find();
        $res = [];
        if (empty($existNote)) {
            if (!empty($reviewFeedbackIds)) {
                $res['review_feedback'] = model(ReviewFeedbackModel::class)->where(['id' => ['IN', $reviewFeedbackIds]])->delete();
            }
            return $res;
        } else {
            if ($existIsStarted) {
                throw new LogicException('review feedback already started', ErrorCode::REVIEW_FEEDBACK_ALREADY_STARTED);
            }
            $note = model(NoteModel::class)->where([
                'review_feedback_id' => ['IN', $reviewFeedbackIds],
                'demand_status' => ["IN", NoteModel::REPAIRED_DEMAND_STATUS]
            ])->find();
            if (!empty($note)) {
                throw new LogicException('review feedback already started', ErrorCode::REVIEW_FEEDBACK_ALREADY_STARTED);
            }
            $noteList = model(NoteModel::class)->where(['review_feedback_id' => ['IN', $reviewFeedbackIds],])->field("id")->select();
            $noteIds = array_column($noteList, "id");

            $relationNoteEntityRelationIds = [];
            $noteEntityRelationIds = [];
            if (!empty($noteIds)) {
                //删除关联关系
                $noteEntityRelationList = model(NoteEntityRelationModel::class)->where(['note_id' => ["IN", $noteIds]])->field("id")->select();

                if (!empty($noteEntityRelationList)) {
                    $noteEntityRelationIds = array_column($noteEntityRelationList, "id");
                    $relationNoteEntityRelationList = model(RelationNoteEntityRelationModel::class)
                        ->where(['note_entity_relation_id' => ["IN", $noteEntityRelationIds]])
                        ->field("id")
                        ->select();
                    //删除影响的镜头
                    $relationNoteEntityRelationIds = array_column($relationNoteEntityRelationList, "id");
                }
            }

            //删除影响的镜头关联关系
            $reviewFeedbackModel->startTrans();
            try {
                if (!empty($relationNoteEntityRelationIds)) {
                    $res['relation_note_entity_relation'] = model(RelationNoteEntityRelationModel::class)->where(["id" => ["IN", $relationNoteEntityRelationIds]])->delete();
                }

                //删除关联关系
                if (!empty($noteEntityRelationIds)) {
                    $res['note_entity_relation'] = model(NoteEntityRelationModel::class)->where(["id" => ["IN", $noteEntityRelationIds]])->delete();
                }
                if (!empty($noteIds)) {
                    $res['note'] = model(NoteModel::class)->where(['id' => ['IN', $noteIds]])->delete();
                }

                if (!empty($reviewFeedbackIds)) {
                    $res['review_feedback'] = model(ReviewFeedbackModel::class)->where(['id' => ['IN', $reviewFeedbackIds]])->delete();
                }
                $reviewFeedbackModel->commit();
                return $res;
            } catch (Throwable $e) {
                $reviewFeedbackModel->rollback();
                throw $e;
            }
        }
    }

    /**
     * 添加审核反馈视频
     * @param $data
     * @return array
     * @throws Throwable
     * @throws Exception
     */
    public function addCosMedia($data)
    {
        $reviewFeedbackId = $data["review_feedback_id"];
        $cosMediaData = $data["cos_media"];
        $reviewFeedbackModel = model(ReviewFeedbackModel::class);
        $cosMedia = $reviewFeedbackModel
            ->field(["JSON_UNQUOTE(JSON_EXTRACT(json, '$.review_feedback_cos_media')) as cos_media"])
            ->find($reviewFeedbackId);

        $cosMediaList = [];
        if (!empty($cosMedia["cos_media"])) {
            $cosMediaList = explode(",", $cosMedia["cos_media"]);
        }
        $reviewFeedbackModule = module_by_code("review_feedback");

        $reviewFeedbackModel->startTrans();
        try {

            $cosMediaResult = CosMediaService::getInstance()->findAddMedia($cosMediaData);
            $cosMediaList[] = $cosMediaResult["id"];
            $cosMediaList = array_unique($cosMediaList);
            $cosMediaIds = join(",", $cosMediaList);

            $result = [
                "id" => $reviewFeedbackId,
                "review_feedback_cos_media" => $cosMediaIds
            ];

            $commonService = CommonService::instance("review_feedback");
            $resData = $commonService->commonUpdate(['data' => $result], $reviewFeedbackModule, false);
            $reviewFeedbackModel->commit();
            $user = model(UserModel::class)->field("id,name")->find($cosMediaResult["created_by"]);
            $cosMediaResult["created_by"] = $user;
            return ["review_feedback" => $resData, "cos_media" => $cosMediaResult];
        } catch (Throwable $e) {
            $reviewFeedbackModel->rollback();
            throw $e;
        }
    }

    /**
     * 删除审核反馈视频
     * @param $data
     * @return array|bool|null
     * @throws Throwable
     * @throws Exception
     */
    public function deleteCosMedia($data)
    {
        $reviewFeedbackId = $data["review_feedback_id"];
        $cosMediaId = $data["cos_media_id"];

        $reviewFeedbackModel = model(ReviewFeedbackModel::class);
        $cosMedia = $reviewFeedbackModel
            ->field(["id,JSON_UNQUOTE(JSON_EXTRACT(json, '$.review_feedback_cos_media')) as cos_media"])
            ->find($reviewFeedbackId);

        if (!empty($cosMedia["cos_media"])) {
            $cosMediaList = explode(",", $cosMedia["cos_media"]);
        } else {
            return $cosMedia;
        }

        $reviewFeedbackModule = module_by_code("review_feedback");

        $cosMediaList = array_filter($cosMediaList, function ($cosMediaItem) use ($cosMediaId) {
            return $cosMediaItem != $cosMediaId;
        });

        $cosMediaIds = join(",", $cosMediaList);
        $result = [
            "id" => $reviewFeedbackId,
            "review_feedback_cos_media" => $cosMediaIds
        ];

        $commonService = CommonService::instance("review_feedback");
        return $commonService->commonUpdate(['data' => $result], $reviewFeedbackModule, false);
    }


    /**
     * 镜头审核反馈列表
     * @param $param
     * @return array
     */
    public function shotReviewFeedbackList($param)
    {
        $filter = $param['filter'];
        $type = $param['type'] ?? "review_feedback";
        $moduleCode = $param['module_code'] ?? "shot";
        $reviewFeedbackId = $filter['review_feedback_id'];
        unset($filter['review_feedback_id']);
        $page = $param["page"] ?? [1, C('default_page_total')];

        $isNote = false;
        $noteFilter = $this->isExistNoteFilter($filter, $isNote);

        if (!isset($noteFilter)) {
            return [];
        }

        $order = $param["order"] ?? null;
        //查询的镜头实体列表
        if ($type == "review_feedback") {
            $shotList = $this->shotReviewFeedbackEntityList($filter, $noteFilter, $isNote, $reviewFeedbackId, $page, $order);
        } else {
            $shotList = $this->getDemandOrderEntityList($reviewFeedbackId, $moduleCode, $filter, $page, $order);
        }

        if (empty($shotList)) {
            return [];
        }

        $storyboardIds = [];
        foreach ($shotList as $shotIndex => $shotListItem) {
            $shotListItem["id"] = intval($shotListItem["id"]);
            $storyboardIds[] = $shotListItem["id"];
            $shotList[$shotIndex] = $shotListItem;
        }
        $storyboardIds = array_unique($storyboardIds);

        $stepCategoryMap = [];
        // 获取镜头相关配置的环节
        $stepCategoryIds = [];
        if ($type == "review_feedback") {
            $reviewFeedbackStepCategoryConfig = OptionsService::getInstance()->getOptionsConfigItemData("review_feedback_step_category_config", "shot");
            $stepCategoryIds = explode(",", $reviewFeedbackStepCategoryConfig);
        } else {
            if ($moduleCode == 'shot') {
                $options = OptionsService::getInstance()->getOptionsData("shot_demand_order_step_category_config");
            } else {
                $options = OptionsService::getInstance()->getOptionsData("asset_demand_order_step_category_config");
            }
            foreach ($options as $optionsItem) {
                if (!empty($optionsItem['step_category_id'])) {
                    $stepCategoryIds[$optionsItem['step_category_id']] = $optionsItem['step_category_id'];
                    $stepCategoryMap[$optionsItem['step_category_id']] = $optionsItem;
                }
            }
            $stepCategoryIds = array_values($stepCategoryIds);
        }

        if (empty($stepCategoryIds)) {
            throw new LogicException("options review feedback step category config not exist", ErrorCode::OPTIONS_REVIEW_FEEDBACK_STEP_CATEGORY_CONFIG_NOT_EXIST);
        }

        $stepCategoryList = model(StepCategoryModel::class)->where(["id" => ["IN", $stepCategoryIds]])->select();
        $defaultStepCategory = $stepCategoryList[0];
        $stepCategoryList = array_column($stepCategoryList, null, "id");

        //获取note根据环节分组
        $noteEntityRelationGroup = $this->getNoteEntityRelationGroupData($noteFilter, $reviewFeedbackId, $storyboardIds, $stepCategoryIds, $stepCategoryList, $moduleCode, $stepCategoryMap);
        //查找相关联的 首先找到相关的镜头 关卡和资产
        $relationFilter = [
            "relation_note_entity_relation.entity_id" => ["IN", $storyboardIds],
            "note.review_feedback_id" => $reviewFeedbackId
        ];
        $relationNoteEntityRelationMap = $this->getRelationAssetEntityList($relationFilter, $type);
        //获取实体-需求  环节外
        $relationNoteText = $this->getRelationNoteText($storyboardIds, $reviewFeedbackId);
        //对各个工序分类进行复制
        $shotList = $this->mergeShotList($shotList, $noteEntityRelationGroup, $relationNoteEntityRelationMap, $defaultStepCategory, $type, $stepCategoryMap, $relationNoteText);
        return $shotList;
    }


    /**
     * 获取优先级高的需求状态
     * @param $oldStatus
     * @param $newStatus
     * @return mixed
     */
    public function getPriorityDemandStatus($oldStatus, $newStatus)
    {
        $demandStatusPriority = [
            NoteModel::DEMAND_STATUS_PROCESSING,
            NoteModel::DEMAND_STATUS_PENDING,
            NoteModel::DEMAND_STATUS_FINAL,
            NoteModel::DEMAND_STATUS_DEFAULT,
            NoteModel::DEMAND_STATUS_REJECTED
        ];

        if (array_search($newStatus, $demandStatusPriority) < array_search($oldStatus, $demandStatusPriority)) {
            return $newStatus;
        }
        return $oldStatus;
    }

    /**
     * 资产审核反馈列表
     * @param $param
     * @return array|false|mixed|string
     */
    public function assetReviewFeedbackList($param)
    {
        $filter = $param["filter"];
        $reviewFeedbackId = $filter["review_feedback_id"];
        $page = $param["page"] ?? [1, C('default_page_total')];

        $order = $param["order"] ?? "note_entity_relation.entity_id,note_entity_relation.step_category_id,note_entity_relation.task_id";
        //是否存在剧集或者资产名字筛选
        $isInitialEpisodeCode = false;
        $this->isExistEpisodeFilter($filter, $isInitialEpisodeCode);

        $isNote = false;
        $noteFilter = $this->isExistNoteFilter($filter, $isNote);

        if (!isset($noteFilter)) {
            return [];
        }

        unset($filter["review_feedback_id"]);
        if (!empty($filter['shot.name']) || $isInitialEpisodeCode) {
            $shotName = $filter['shot.name'] ?? "";
            unset($filter['shot.name']);

            $relationFilter = [
                'note_entity_relation.review_feedback_id' => $reviewFeedbackId,
                'note_entity_relation.relate_module_code' => "asset",
            ];


            $relationNoteEntityRelation = model(RelationNoteEntityRelationModel::class);
            //如果有镜头名字筛选
            if (!empty($shotName)) {
                $relationNoteEntityRelation = $relationNoteEntityRelation->join("entity shot on relation_note_entity_relation.entity_id = shot.id");
                $relationFilter['shot.name'] = $shotName;
            }

            $relationNoteEntityRelation = $relationNoteEntityRelation
                ->join("note_entity_relation on note_entity_relation.id = relation_note_entity_relation.note_entity_relation_id");

            //如果有剧集或者资产名字的筛选
            if ($isInitialEpisodeCode) {
                $relationNoteEntityRelation = $relationNoteEntityRelation->join("entity on note_entity_relation.entity_id = entity.id");
            }

            if ($isNote) {
                $relationNoteEntityRelation = $relationNoteEntityRelation
                    ->join("note on note_entity_relation.note_id = note.id")
                    ->where($noteFilter);
            }


            $note = $relationNoteEntityRelation
                ->where($relationFilter)
                ->where($filter)
                ->field("note_entity_relation.entity_id,note_entity_relation.relation_type,note_entity_relation.step_category_id,note_entity_relation.task_id")
                ->group("note_entity_relation.entity_id,note_entity_relation.relation_type,note_entity_relation.step_category_id,note_entity_relation.task_id")
                ->order($order)
                ->page(...$page)
                ->select();

        } elseif ($isNote) {
            $note = model(NoteEntityRelationModel::class)
                ->join("note on note.id = note_entity_relation.note_id")
                ->where(['note_entity_relation.review_feedback_id' => $reviewFeedbackId, 'note_entity_relation.relate_module_code' => "asset"])
                ->where($noteFilter)
                ->field("note_entity_relation.entity_id,note_entity_relation.relation_type,note_entity_relation.step_category_id,note_entity_relation.task_id")
                ->group("note_entity_relation.entity_id,note_entity_relation.relation_type,note_entity_relation.step_category_id,note_entity_relation.task_id")
                ->order($order)
                ->page(...$page)
                ->select();
        } else {
            //以工序来分页
            $note = model(NoteEntityRelationModel::class)
                ->where(['note_entity_relation.review_feedback_id' => $reviewFeedbackId, 'note_entity_relation.relate_module_code' => "asset",])
                ->field("note_entity_relation.entity_id,note_entity_relation.relation_type,note_entity_relation.step_category_id,note_entity_relation.task_id")
                ->group("note_entity_relation.entity_id,note_entity_relation.relation_type,note_entity_relation.step_category_id,note_entity_relation.task_id")
                ->order($order)
                ->page(...$page)
                ->select();
        }

        if (empty($note)) {
            return [];
        }

        $entityIds = [];
        $taskIds = [];
        $stepCategoryIds = [];
        $stepCategoryWhere = [];
        foreach ($note as $noteItem) {
            $entityIds[] = $noteItem['entity_id'];
            $stepCategoryIds[] = $noteItem['step_category_id'];
            if ($noteItem['relation_type'] === NoteEntityRelationModel::RELATION_TYPE_STEP) {
                $taskIds[] = $noteItem['task_id'];
            } else {
                if (empty($stepCategoryWhere[$noteItem["entity_id"]])) {
                    $stepCategoryWhere[$noteItem["entity_id"]] = [$noteItem['step_category_id']];
                } elseif (!in_array($noteItem['step_category_id'], $stepCategoryWhere[$noteItem["entity_id"]])) {
                    $stepCategoryWhere[$noteItem["entity_id"]][] = $noteItem['step_category_id'];
                }
            }
        }
        $entityIds = array_unique($entityIds);
        $stepCategoryIds = array_unique($stepCategoryIds);

        $stepCategoryFilterWhere = [];
        if (!empty($stepCategoryWhere)) {
            foreach ($stepCategoryWhere as $stepCategoryWhereIndex => $stepCategoryWhereItem) {
                $stepCategoryFilterWhere[] = [
                    "entity_id" => $stepCategoryWhereIndex,
                    "step_category_id" => ["IN", $stepCategoryWhereItem],
                ];
                $stepCategoryFilterWhere["_logic"] = "OR";
            }

            $stepCategoryFilterWhere = [
                "relation_type" => NoteEntityRelationModel::RELATION_TYPE_STEP_CATEGORY,
                $stepCategoryFilterWhere
            ];
        }


        $taskList = [];
        $taskFilterWhere = [];
        if (!empty($taskIds)) {
            $taskList = model(TaskModel::class)->where(['id' => ["IN", $taskIds]])->field(["task.id as task_id", "task.name as task_name", "task.step_category_id"])->select();
            $taskList = array_column($taskList, null, 'task_id');

            $taskFilterWhere = [
                "relation_type" => NoteEntityRelationModel::RELATION_TYPE_STEP,
                "task_id" => ["IN", $taskIds]
            ];
        }

        $entityList = model(EntityModel::class)->where(['id' => ["IN", $entityIds]])
            ->field(["id", "name", "initial_episode_code", "showings_number", "module_id", "json"])
            ->select();

        $entityListData = [];
        foreach ($entityList as $entityItem) {
            $entityItem["category"] = null;
            if (!empty($entityItem["json"])) {
                $entityJson = json_decode($entityItem["json"], true);
                unset($entityItem["json"]);
                $entityItem["category"] = $entityJson[module_by_id($entityItem["module_id"])["code"] . "_category"] ?? null;
            }

            $entityListData[$entityItem["id"]] = $entityItem;
        }

        $stepCategoryList = model(StepCategoryModel::class)->where(["id" => ["IN", $stepCategoryIds]])->field("id,name")->select();
        $stepCategoryList = array_column($stepCategoryList, null, "id");

        $noteFields = [
            "note_entity_relation.id", "note_entity_relation.note_id", "note.text", "note.mark_modify", "note.demand_status",
            "note.demand_status as accept_changes", "note.demand_status as repair_progress",
            "JSON_UNQUOTE(JSON_EXTRACT(note.json, '$.note_cos_media')) as cos_media",
            "JSON_UNQUOTE(JSON_EXTRACT(note.json, '$.reason_type')) as reason_type",
            "note_entity_relation.task_id", "note_entity_relation.relation_type", "note_entity_relation.entity_id", "note_entity_relation.step_category_id"
        ];

        $noteEntityRelationListWhere = [
            "note_entity_relation.review_feedback_id" => $reviewFeedbackId,
            'note_entity_relation.relate_module_code' => "asset"
        ];

        if (!empty($taskFilterWhere) && !empty($stepCategoryFilterWhere)) {
            $noteEntityRelationListWhere[] = [$taskFilterWhere, $stepCategoryFilterWhere, "_logic" => "OR"];
        } else {
            if (!empty($taskFilterWhere)) {
                $noteEntityRelationListWhere[] = $taskFilterWhere;
            }

            if (!empty($stepCategoryFilterWhere)) {
                $noteEntityRelationListWhere[] = $stepCategoryFilterWhere;
            }

            $noteEntityRelationListWhere["_logic"] = "AND";
        }

        //查询相关 需求意见

        $noteEntityRelationList = model(NoteEntityRelationModel::class)
            ->join("note on note.id = note_entity_relation.note_id")
            ->where($noteEntityRelationListWhere)
            ->where($noteFilter)
            ->field($noteFields)
            ->select();


        $noteEntityRelationIds = [];
        $cosMedias = [];
        foreach ($noteEntityRelationList as $noteEntityRelationIndex => $noteEntityRelationItem) {
            $noteCosMedia = [];
            if (!empty($noteEntityRelationItem["cos_media"])) {
                $noteCosMedia = explode(",", $noteEntityRelationItem["cos_media"]);
            }

            $cosMedias = array_merge($cosMedias, $noteCosMedia);
            $noteEntityRelationItem["cos_media"] = $noteCosMedia;
            $noteEntityRelationIds[] = $noteEntityRelationItem["id"];
            $noteEntityRelationList[$noteEntityRelationIndex] = $noteEntityRelationItem;
        }

        //获取受影响的镜头
        $relationNoteEntityRelationList = model(RelationNoteEntityRelationModel::class)
            ->where(["note_entity_relation_id" => ["IN", $noteEntityRelationIds]])
            ->field("id,entity_id,note_entity_relation_id")
            ->select();

        $relationNoteEntityRelationMap = [];
        foreach ($relationNoteEntityRelationList as $relationNoteEntityRelationItem) {
            if (!isset($relationNoteEntityRelationMap[$relationNoteEntityRelationItem["note_entity_relation_id"]])) {
                $relationNoteEntityRelationMap[$relationNoteEntityRelationItem["note_entity_relation_id"]] = [$relationNoteEntityRelationItem["entity_id"] => $relationNoteEntityRelationItem["entity_id"]];
            } elseif (!isset($relationNoteEntityRelationMap[$relationNoteEntityRelationItem["note_entity_relation_id"]][$relationNoteEntityRelationItem["entity_id"]])) {
                $relationNoteEntityRelationMap[$relationNoteEntityRelationItem["note_entity_relation_id"]][$relationNoteEntityRelationItem["entity_id"]] = $relationNoteEntityRelationItem["entity_id"];
            }
        }

        $noteEntityRelationMap = [];
        $assetRelationShotList = [];
        foreach ($noteEntityRelationList as $noteEntityRelationItem) {
            $cosMediaTmp = [];

            if ($noteEntityRelationItem["relation_type"] === NoteEntityRelationModel::RELATION_TYPE_STEP_CATEGORY) {
                $noteEntityRelationKey = join("_", [$noteEntityRelationItem["relation_type"], $noteEntityRelationItem["entity_id"], $noteEntityRelationItem["step_category_id"]]);
            } else {
                $noteEntityRelationKey = join("_", [$noteEntityRelationItem["relation_type"], $noteEntityRelationItem["entity_id"], $noteEntityRelationItem["task_id"]]);
            }


            if (!empty($noteEntityRelationItem["cos_media"])) {
                foreach ($noteEntityRelationItem["cos_media"] as $cosMediaId) {
                    if (isset($cosMediaList[$cosMediaId])) {
                        $cosMediaTmp[] = $cosMediaList[$cosMediaId];
                    }
                }
            }

            $noteEntityRelationItem["cos_media"] = $cosMediaTmp;
            // 计算影响镜头个数
            if (isset($relationNoteEntityRelationMap[$noteEntityRelationItem["id"]])) {
                if (!isset($assetRelationShotList[$noteEntityRelationKey])) {
                    $assetRelationShotList[$noteEntityRelationKey] = $relationNoteEntityRelationMap[$noteEntityRelationItem["id"]];
                } else {
                    $assetRelationShotList[$noteEntityRelationKey] = $assetRelationShotList[$noteEntityRelationKey] + $relationNoteEntityRelationMap[$noteEntityRelationItem["id"]];
                }
            }
            $noteEntityRelationMap[$noteEntityRelationKey][] = $noteEntityRelationItem;
        }

        foreach ($assetRelationShotList as $assetRelationShotTaskId => $assetRelationShotItem) {
            $assetRelationShotList[$assetRelationShotTaskId] = count($assetRelationShotItem);
        }

        foreach ($note as $noteIndex => $noteItem) {

            if ($noteItem["relation_type"] === NoteEntityRelationModel::RELATION_TYPE_STEP_CATEGORY) {
                $noteEntityRelationKey = join("_", [$noteItem["relation_type"], $noteItem["entity_id"], $noteItem["step_category_id"]]);
            } else {
                $noteEntityRelationKey = join("_", [$noteItem["relation_type"], $noteItem["entity_id"], $noteItem["task_id"]]);
            }

            $noteItem['key'] = join("_", [$noteItem["relation_type"], $noteItem["entity_id"], $noteItem["step_category_id"], $noteItem["task_id"]]);

            $noteItem = array_merge($noteItem, $entityListData[$noteItem["entity_id"]], $taskList[$noteItem['task_id']] ?? []);
            $noteItem["step_category_name"] = $stepCategoryList[$noteItem["step_category_id"]]["name"];
            $noteItem["affected_shot_count"] = $assetRelationShotList[$noteEntityRelationKey] ?? 0;
            $noteItem["repair_demand"] = $noteEntityRelationMap[$noteEntityRelationKey] ?? [];
            $note[$noteIndex] = $noteItem;
        }

        return $note;
    }

    /**
     * 获取发起返修列表
     * @param $param
     * @return array|false|mixed|string
     */
    public function getSendRepairList($param)
    {
        $filter = $param["filter"];
        $noteIds = $filter["note_ids"];
        $moduleCode = "asset";

        $noteFields = ["note_entity_relation.entity_id", "note.module_code", "note_entity_relation.step_category_id", "note_entity_relation.task_id", "note_entity_relation.relation_type",
            "note.id", "note.text", "JSON_UNQUOTE(JSON_EXTRACT(note.json, '$.reason_type')) as reason_type", "note.review_feedback_id", "note.demand_status as repair_progress"];

        // 只有待发起返修的才能返修
        $noteList = model(NoteModel::class)
            ->join("note_entity_relation on note.id = note_entity_relation.note_id")
            ->where(["note.id" => ["IN", $noteIds], "note.demand_status" => NoteModel::DEMAND_STATUS_PENDING])
            ->field($noteFields)
            ->select();

        if (empty($noteList)) {
            return [];
        }

        if (!empty($noteList[0]["module_code"])) {
            $moduleCode = $noteList[0]["module_code"];
        }

        $taskIds = [];
        $stepCategoryIds = [];
        $entityIds = [];
        $entityStepCategoryMapWhere = [];
        $entityStepCategoryMap = [];

        foreach ($noteList as $noteItem) {
            $stepCategoryIds[] = $noteItem["step_category_id"];
            $entityIds[] = $noteItem["entity_id"];

            if ($noteItem["relation_type"] === NoteEntityRelationModel::RELATION_TYPE_STEP_CATEGORY) {
                if (!isset($entityStepCategoryMap[$noteItem["entity_id"]])) {
                    $entityStepCategoryMap[$noteItem["entity_id"]] = [$noteItem["step_category_id"]];
                } elseif (!in_array($noteItem["step_category_id"], $entityStepCategoryMap[$noteItem["entity_id"]])) {
                    $entityStepCategoryMap[$noteItem["entity_id"]][] = $noteItem["step_category_id"];
                }
            } else {
                $taskIds[] = $noteItem["task_id"];
            }
        }

        $entityIds = array_unique($entityIds);

        $entityMapList = [];
        $entityIdMapList = [];
        // 通过分镜查找到镜头
        if ($moduleCode === "shot") {
            $storyboardList = model(EntityModel::class)
                ->where(["id" => ["IN", $entityIds]])
                ->field("id,project_id,initial_episode_code,showings_number,name,code")
                ->select();

            $storyboardWhere = [];
            foreach ($storyboardList as $storyboardItem) {
                $storyboardWhere[] = [
                    "initial_episode_code" => $storyboardItem["initial_episode_code"],
                    "showings_number" => $storyboardItem["showings_number"],
                    "code" => $storyboardItem["code"],
                ];
            }

            $storyboardWhere["_logic"] = "OR";

            $shotList = model(EntityModel::class)->where([
                $storyboardWhere,
                "project_id" => $storyboardList[0]["project_id"],
                "module_id" => module_by_code("shot")['id']
            ])->field("id,initial_episode_code,showings_number,name,JSON_UNQUOTE(JSON_EXTRACT(json,'$.shot_storyboard')) as shot_storyboard")->select();


            foreach ($shotList as $shotListItem) {
                $shotListItem['parent_entity_id'] = $shotListItem['shot_storyboard'];
                $entityMapList[$shotListItem['shot_storyboard']] = $entityIdMapList[$shotListItem['id']] = $shotListItem;
            }


        } else {
            $designList = model(EntityModel::class)
                ->where(["id" => ["IN", $entityIds]])
                ->field("id,project_id,code")
                ->select();

            $designCodes = array_column($designList, 'code');

            // 被返修的任务只能是资产与关卡里的
            $assetList = model(EntityModel::class)->where([
                "project_id" => $designList[0]["project_id"],
                "code" => ["IN", $designCodes],
                "module_id" => ["IN", [module_by_code("asset")['id'], module_by_code("level")['id']]]
            ])->field("id,name,showings_number,initial_episode_code,module_id,json")->select();

            //概念与关卡一一对应
            foreach ($assetList as $assetItem) {
                if (!empty($assetItem['json'])) {
                    $assetJson = json_decode($assetItem['json'], true);

                    $currentDesignId = $assetJson[module_by_id($assetItem['module_id'])['code'] . '_design'];
                    unset($assetItem['json']);
                    $assetItem['parent_entity_id'] = $currentDesignId;
                    $entityMapList[$currentDesignId] = $entityIdMapList[$assetItem['id']] = $assetItem;
                }
            }
        }


        //通过概念对应的资产,组装出任务的查询的条件
        if (!empty($entityStepCategoryMap)) {
            foreach ($entityStepCategoryMap as $entityId => $entityStepCategoryItem) {
                if (!isset($entityMapList[$entityId])) {
                    continue;
                }
                $entityStepCategoryMapWhere[] = [
                    "entity_id" => $entityMapList[$entityId]['id'],
                    "step_category_id" => ["IN", $entityStepCategoryItem]
                ];
            }
            if (!empty($entityStepCategoryMapWhere)) {
                $entityStepCategoryMapWhere["_logic"] = "OR";
            }

        }

        if (!empty($taskIds)) {
            $taskIds = array_unique($taskIds);
            if (!empty($entityStepCategoryMapWhere)) {
                $entityStepCategoryMapWhere = [
                    $entityStepCategoryMapWhere,
                    'id' => ["IN", $taskIds],
                    "_logic" => "OR"
                ];
            } else {
                $entityStepCategoryMapWhere = ['id' => ["IN", $taskIds]];
            }
        }


        $notStartedStatus = StatusService::getInstance()->getNotStartedStatus();
        $notStartedStatus = array_column($notStartedStatus, 'id');

        $taskList = model(TaskModel::class)->where([$entityStepCategoryMapWhere, "step_type" => 'step'])
            ->field("id,name,project_id,entity_id,step_id,step_category_id,JSON_UNQUOTE(JSON_EXTRACT(json,'$.task_status')) as task_status")
            ->select();

        // noteId => task_list
        $taskMap = [];
        $entityTaskListMap = [];
        foreach ($taskList as $taskItem) {
            if (in_array($taskItem['task_status'], $notStartedStatus)) {
                continue;
            }

            if (isset($entityIdMapList[$taskItem['entity_id']]) &&
                isset($entityStepCategoryMap[$entityIdMapList[$taskItem['entity_id']]['parent_entity_id']]) &&
                in_array($taskItem['step_category_id'], $entityStepCategoryMap[$entityIdMapList[$taskItem['entity_id']]['parent_entity_id']])
            ) {
                $entityTaskListMap[$entityIdMapList[$taskItem['entity_id']]['parent_entity_id']][$taskItem['step_category_id']][] = $taskItem;
            }

            $taskMap[$taskItem['id']] = $taskItem;
        }


        $stepCategoryIds = array_unique($stepCategoryIds);
        $StepCategoryList = model(StepCategoryModel::class)->where(["id" => ["IN", $stepCategoryIds]])->select();
        $StepCategoryList = array_column($StepCategoryList, null, "id");

        foreach ($noteList as $noteIndex => $noteItem) {
            $noteItem["step_category_name"] = $StepCategoryList[$noteItem["step_category_id"]]["name"];
            if ($noteItem['module_code'] == 'shot') {
                $noteItem["step_category_code"] = $StepCategoryList[$noteItem["step_category_id"]]["code"];
            }
            $noteItem["entity"] = $entityMapList[$noteItem["entity_id"]];
            $noteItem["task_list"] = [];

            if ($noteItem["relation_type"] === NoteEntityRelationModel::RELATION_TYPE_STEP_CATEGORY) {
                $noteItem["task_list"] = $entityTaskListMap[$noteItem['entity_id']][$noteItem['step_category_id']] ?? [];
            } else {
                if (!isset($taskMap[$noteItem["task_id"]])) {
                    unset($noteList[$noteIndex]);
                    continue;
                }
                $noteItem["task_list"] = [$taskMap[$noteItem["task_id"]]];
            }
            $noteList[$noteIndex] = $noteItem;
        }

        return $noteList;
    }

    /**
     * 获取受影响的镜头
     * @param $filter
     * @return array|array[]
     */
    public function getAffectedShotList($filter)
    {
        $noteEntityRelationIds = model(NoteEntityRelationModel::class)
            ->where($filter)
            ->where(["relate_module_code" => "asset"])
            ->field("id")
            ->select();

        if (empty($noteEntityRelationIds)) {
            return [];
        }

        $noteEntityRelationIds = array_unique(array_column($noteEntityRelationIds, "id"));


        return $this->getAffectedShotData(["IN", $noteEntityRelationIds]);
    }

    /**
     * 获取审核反馈工序分类列表
     * @param $reviewFeedbackId
     * @param $moduleCode
     * @param $episodeCode
     * @param string $type
     * @return array|mixed
     */
    public function getReviewFeedbackStepCategoryList($reviewFeedbackId, $moduleCode, $episodeCode, $type = 'review_feedback')
    {
        $res = [];
        if ($type == 'review_feedback') {
            if ($moduleCode === "shot") {
                $reviewFeedbackStepCategoryConfig = OptionsService::getInstance()->getOptionsData("review_feedback_shot_step_category_config");
                $res = $reviewFeedbackStepCategoryConfig;

                $reviewFeedback = model(ReviewFeedbackModel::class)->field("project_id,episode_codes")->find($reviewFeedbackId);

                $stepCategoryIds = [];
                $stepCategoryNameMap = [];
                foreach ($res as $resItem) {
                    if (!empty($resItem['step_category_id'])) {
                        $stepCategoryIds[$resItem['step_category_id']] = $resItem['step_category_id'];
                        $stepCategoryNameMap[$resItem['step_category_id']] = $resItem['name'];
                    }
                }
                $stepCategoryIds = array_values($stepCategoryIds);
                $count = $this->getStepCategoryCount($reviewFeedbackId, $episodeCode, $reviewFeedback['project_id'], $stepCategoryIds);
                $resultCount = $count['result_count'];
                $markModifyCount = $count['mark_modify_count'];
                $demandStatusCount = $count['demand_status_count'];
                $repairCount = $count['repair_count'];
                $shotCount = $count['shot_count'];
                $stepCategoryCount = $count['step_category_count'];
                $assetRepairCount = $count['asset_repair_count'];

                $stepCategoryCountGroup = [];
                foreach ($resultCount as $resultCountIndex => $resultCountData) {
                    foreach ($resultCountData as $resultCountDataIndex => $resultCountDataItem) {
                        $stepCategoryCountGroup[$resultCountIndex][] = [
                            'category_count' => $resultCountDataItem,
                            'demand_status_count' => $demandStatusCount[$resultCountIndex][$resultCountDataIndex] ?? 0,
                            'mark_modify_count' => $markModifyCount[$resultCountIndex][$resultCountDataIndex] ?? 0,
                            'reason_type' => $resultCountDataIndex,
                            'shot_count' => $stepCategoryCount[$resultCountIndex] ?? 0,
                            'step_category_id' => $resultCountIndex
                        ];
                    }
                }

                $res = array_map(function ($resItem) use ($stepCategoryCountGroup, $shotCount, $repairCount, $stepCategoryCount, $assetRepairCount, $stepCategoryNameMap) {
                    if ($resItem["prop"] == 'shot') {
                        $resItem["shot_count"] = $shotCount;
                        $resItem["step_category_count"] = [];
                        foreach ($stepCategoryNameMap as $stepCategoryId => $stepCategoryNameMapItem) {
                            $resItem["step_category_count"][] = [
                                "step_category_id" => $stepCategoryId,
                                "name" => $stepCategoryNameMapItem,
                                "category_count" => $stepCategoryCount[$stepCategoryId] ?? 0
                            ];
                        }
                    }
                    if ($resItem["prop"] == 'asset') {
                        $resItem["step_category_percentage"] = [
                            "repair_count" => $assetRepairCount['asset'],
                        ];
                    }
                    if ($resItem["prop"] == 'level') {
                        $resItem["step_category_percentage"] = [
                            "repair_count" => $assetRepairCount['level'],
                        ];
                    }
                    if (!empty($resItem["step_category_id"])) {
                        $resItem["step_category_percentage"] = [
                            "repair_count" => $repairCount[$resItem["step_category_id"]] ?? 0,
                            "category_list" => $stepCategoryCountGroup[$resItem["step_category_id"]] ?? [],
                            'shot_count' => $stepCategoryCount[$resItem["step_category_id"]] ?? 0
                        ];
                    }
                    return $resItem;
                }, $res);
            } else {
                $reviewFeedbackStepCategoryConfig = OptionsService::getInstance()->getOptionsData("review_feedback_asset_step_category_config");
                $requestData = [
                    'review_feedback_id' => intval($reviewFeedbackId)
                ];

                $shotCountData = teamones_request('teamones-olap-sr', 'note_entity_relation/get_asset_feedback_affected_shot_count', $requestData);

                $affectedShotCount = 0;
                if (!empty($shotCountData) && !empty($shotCountData['data'])) {
                    $affectedShotCount = $shotCountData['data']['affected_shot_count'] ?? 0;
                }

                $reviewFeedbackStepCategoryConfig['affected_shot_count'] = $affectedShotCount;
                $res = $reviewFeedbackStepCategoryConfig;
            }
        } else {
            list($assetRepairCount, $stepCategoryNameMap, $noConfirmNoteCount, $stepCategoryCount, $singlePlanMap,
                $res, $moduleCount, $stepWorkflowMap, $affectedShotCount, $reviewFeedback, $stepCategoryDownstreamList, $episodeCodeMap,
                $stepCategoryRatioMap, $stepCategoryAppendCount, $stepCategoryRepairCount) =
                $this->getDemandOrderStepCategoryList($reviewFeedbackId, $moduleCode, $episodeCode);

            foreach ($res as $resIndex => $resItem) {
                if ($resItem["prop"] == 'shot_name') {
                    $resItem["step_category_count"] = [];
                    foreach ($stepCategoryNameMap as $stepCategoryId => $stepCategoryNameMapItem) {
                        $resItem["step_category_count"][] = [
                            "step_category_id" => $stepCategoryId,
                            "name" => $stepCategoryNameMapItem,
                            "category_count" => $stepCategoryCount[$stepCategoryId] ?? 0
                        ];
                    }
                }

                //补充影响镜头个数
                if ($resItem["prop"] == 'asset_affected_shot_count') {
                    $resItem["count"] = $affectedShotCount;
                }

                if (in_array($resItem["prop"], ['shot_name', 'asset_name'])) {
                    $resItem["count"] = $moduleCount;
                }

                if (isset($assetRepairCount[$resItem['prop']])) {
                    $resItem['count'] = $assetRepairCount[$resItem['prop']];
                }
                if (!empty($resItem['step_category_id'])) {
                    $resItem['plan'] = $singlePlanMap[$resItem['step_category_id']] ?? null;
                    $resItem['plan_delivery_time'] = 0;
                    if (!empty($episodeCodeMap[$episodeCode])) {
                        $resItem['plan_delivery_time'] = intval($reviewFeedback['json']['plan_delivery_time'][$episodeCodeMap[$episodeCode]['id']][$resItem['step_category_id']] ?? 0);
                    }

                    //统计
                    $resItem['count'] = $noConfirmNoteCount[$resItem['step_category_id']]['count'] ?? 0;
                    $resItem['no_confirm_count'] = $noConfirmNoteCount[$resItem['step_category_id']]['no_confirm_count'] ?? 0;

                    //实体总数
                    $resItem['entity_total_count'] = $moduleCount;
                    //添加新增实体统计 ,返修实体统计 ,给与阶段比例
                    //阶段比例
                    $resItem['add_percentage'] = $stepCategoryRatioMap[$resItem['step_category_id']]['add_percentage'] ?? 0;
                    $resItem['edit_percentage'] = $stepCategoryRatioMap[$resItem['step_category_id']]['edit_percentage'] ?? 0;

                    //返修实体统计
                    $resItem['repair_entity_count'] = $stepCategoryRepairCount[$resItem['step_category_id']] ?? 0;
                    //新增实体统计
                    $resItem['append_entity_count'] = $stepCategoryAppendCount[$resItem['step_category_id']] ?? 0;
                }

                if (!empty($resItem['children_step_category_id'])) {
                    foreach ($resItem['children'] as $childrenIndex => $childrenItem) {
                        //属性为返修工序的
                        if ($childrenItem["prop"] == $resItem["prop"] . "_" . "repair_step_id") {
                            $resItem['children'][$childrenIndex]['step_list'] = [];
                            foreach ($resItem['children_step_category_id'] as $childrenStepCategoryId) {
                                if (isset($stepWorkflowMap[$childrenStepCategoryId])) {
                                    $resItem['children'][$childrenIndex]['step_list'] =
                                        array_merge($resItem['children'][$childrenIndex]['step_list'], $stepWorkflowMap[$childrenStepCategoryId]);
                                }
                            }
                        }
                    }
                    $resItem['downstream_step_category_list'] = $stepCategoryDownstreamList[$resItem['step_category_id']] ?? [];
                }
                $res[$resIndex] = $resItem;
            }
        }

        return $res;
    }

    /**
     * 获取审核反馈的实体的工序分类
     * @param $filter
     * @return array|false|mixed|string
     */
    public function getEntityReviewFeedbackStepCategoryList($filter)
    {
        $moduleCode = $filter["module_code"];
        if ($moduleCode == "shot") {
            $reviewFeedbackStepCategoryConfig = OptionsService::getInstance()->getOptionsConfigItemData("review_feedback_step_category_config", "shot");
            $reviewFeedbackStepCategoryIds = explode(",", $reviewFeedbackStepCategoryConfig);

            if (empty($reviewFeedbackStepCategoryIds)) {
                throw new LogicException("options review feedback step category config not exist", ErrorCode::OPTIONS_REVIEW_FEEDBACK_STEP_CATEGORY_CONFIG_NOT_EXIST);
            }

            $stepCategoryList = model(StepCategoryModel::class)->where(["id" => ["IN", $reviewFeedbackStepCategoryIds]])->field("id,name,code,color")->select();
        } else {
            $moduleId = module_by_code($moduleCode)["id"];
            $stepCategory = model(StepCategoryModel::class)->where(["entity_id" => $moduleId, 'parent_id' => 0,])->find();
            $stepCategoryList = model(StepCategoryModel::class)->where(["parent_id" => $stepCategory["id"]])->field("id,name,code,color")->select();
        }
        return $stepCategoryList;
    }

    /**
     * 添加审核反馈
     * @param $data
     * @param bool $resultWanted
     * @return array
     * @throws Exception
     * @throws Throwable
     */
    public function addRepairFeedbackNote($data, $resultWanted = true)
    {
        $note = $data["note"];
        // 镜头查询

        //如果是更新, 则只更新内容
        if (!empty($note["id"])) {
            $result = $this->updateRepairFeedbackNote($data);
        } else {
            // 验证权限
            $type = ReviewFeedbackModel::DEMAND_TYPE_REVIEW_FEEDBACK;
            $reviewFeedback = null;
            if (!empty($data["note"]['review_feedback_id'])) {
                $reviewFeedback = model(ReviewFeedbackModel::class)->find($data["note"]['review_feedback_id']);
                $type = $reviewFeedback['demand_type'];
                $currentPermissions = $reviewFeedback['permissions'];
                $this->reviewFeedbackPermissionsDeal($currentPermissions, "entry_review_feedback_note");
            }
            if (empty($data["note"]["demand_type"])) {
                $data["note"]["demand_type"] = NoteModel::DEMAND_TYPE_REVIEW_FEEDBACK;
            }
            $noteRelation = $data["note_relation"];
            unset($data['note_relation']);
            $entityIdList = [];
            if ($note["module_code"] == "shot" && isset($noteRelation['relation_list'])) {
                $noteRelationList = $noteRelation['relation_list'] ?? [];
                $noteRelationExtraData = $noteRelation['extra_data'];
                $shotIds = [];
                $entityWhere = [];

                $entityModuleId = [];
                foreach ($noteRelationList as $noteRelationItem) {
                    if ($noteRelationItem["relation_type"] == "episode") {

                        $entityModuleId = [module_by_code("shot")["id"]];
                        $entityWhere[] = [
                            "entity.initial_episode_code" => $noteRelationItem["episode_code"],
                        ];
                    } elseif ($noteRelationItem["relation_type"] == "scene") {
                        $entityModuleId = [module_by_code("shot")["id"]];
                        $entityWhere[] = [
                            "entity.initial_episode_code" => $noteRelationItem["episode_code"],
                            "entity.showings_number" => $noteRelationItem["showings_number"]
                        ];
                    } elseif ($noteRelationItem["relation_type"] == "entity") {
                        $entityModuleId = [module_by_code("shot")["id"], module_by_code("storyboard")['id']];
                        if ($type == ReviewFeedbackModel::DEMAND_TYPE_DEMAND_ORDER) {
                            $entityModuleId = [module_by_code("storyboard")['id']];
                            // 需要检查实体是否发布
                            $entityIdList[$noteRelationItem["entity_id"]] = $noteRelationItem["entity_id"];
                        }
                        $entityWhere[] = ["entity.id" => $noteRelationItem["entity_id"]];
                    }
                }

                if ($type == ReviewFeedbackModel::DEMAND_TYPE_DEMAND_ORDER && !empty($reviewFeedback)) {
                    $reviewFeedbackEntityList = model(ReviewFeedbackEntityModel::class)->where(['review_feedback_id' => $reviewFeedback['id'], 'entity_id' => ['IN', implode(',', $entityIdList)]])->select();

                    if (count($reviewFeedbackEntityList) !== count($entityIdList)) {
                        throw new LogicException('review feedback entity not found', ErrorCode::REVIEW_FEEDBACK_ENTITY_NOT_FOUND);
                    }
                    $reviewFeedbackEntityPublished = array_filter($reviewFeedbackEntityList, function ($i) {
                        return $i['published'] === ReviewFeedbackEntityModel::PUBLISHED_YES;
                    });
                    if (!empty($reviewFeedbackEntityPublished)) {
                        throw new LogicException("review feedback entity is published", ErrorCode::REVIEW_FEEDBACK_ENTITY_IS_PUBLISHED);
                    }
                }
                $storyboardMap = [];
                if (!empty($entityWhere)) {
                    $entityWhere["_logic"] = "OR";
                    $storyboardMap = $this->getAddRepairFeedbackNoteShotList($entityWhere, $entityModuleId, $note["project_id"], $noteRelationExtraData['step_category_id'], $type);
                }

                if (empty($storyboardMap)) {
                    return [];
                }

                //重新组装数据
                $res = [];
                foreach ($storyboardMap as $storyboardMapItem) {
                    $noteRelationExtraData = array_merge(
                        $noteRelationExtraData, [
                            "project_id" => $note["project_id"],
                            "entity_id" => $storyboardMapItem["id"],
                            "episode_code" => $storyboardMapItem["initial_episode_code"],
                            "relation_type" => "step_category",
                            "relate_module_code" => "shot"
                        ]
                    );

                    $newData = $data;
                    $newData["note_entity_relation"] = [$noteRelationExtraData];
                    $res[] = $newData;
                }

                // 数量过多,改由队列发送
                $limit = 20;
                $result = $this->addShotRepairFeedbackNote($res, $storyboardMap, $limit);
            } else {
                if ($type == ReviewFeedbackModel::DEMAND_TYPE_DEMAND_ORDER && !empty($reviewFeedback)) {
                    $reviewFeedbackEntity = model(ReviewFeedbackEntityModel::class)->where(['review_feedback_id' => $reviewFeedback['id'], 'entity_id' => $noteRelation['entity_id']])->find();
                    if (empty($reviewFeedbackEntity)) {
                        throw new LogicException('review feedback entity not found', ErrorCode::REVIEW_FEEDBACK_ENTITY_NOT_FOUND);
                    }
                    if ($reviewFeedbackEntity['published'] === ReviewFeedbackEntityModel::PUBLISHED_YES) {
                        throw new LogicException("review feedback entity is published", ErrorCode::REVIEW_FEEDBACK_ENTITY_IS_PUBLISHED);
                    }
                }


                $entity = model(EntityModel::class)
                    ->field("id,name,initial_episode_code,showings_number,module_id,json,id as entity_id,project_id")
                    ->find($noteRelation["entity_id"]);

                if ($noteRelation['type'] === NoteEntityRelationModel::RELATION_TYPE_STEP) {
                    $data["note_entity_relation"] = [
                        [
                            "relation_type" => NoteEntityRelationModel::RELATION_TYPE_STEP,
                            "task_id" => $noteRelation['task_id'],
                            "episode_code" => $noteRelation["initial_episode_code"],
                            "entity_id" => $noteRelation["entity_id"],
                            "project_id" => $note["project_id"],
                            "relate_module_code" => $note["module_code"]
                        ]
                    ];

                    $task = model(TaskModel::class)->field("id as task_id,step_category_id,name as task_name")->find($noteRelation['task_id']);

                } elseif ($noteRelation['type'] === NoteEntityRelationModel::RELATION_TYPE_ENTITY) {
                    $data["note_entity_relation"] = [
                        [
                            "relation_type" => NoteEntityRelationModel::RELATION_TYPE_ENTITY,
                            "episode_code" => $noteRelation["initial_episode_code"],
                            "entity_id" => $noteRelation["entity_id"],
                            "project_id" => $note["project_id"],
                            "relate_module_code" => $note["module_code"]
                        ]
                    ];
                    $task = [];
                } else {
                    $data["note_entity_relation"] = [
                        [
                            "relation_type" => NoteEntityRelationModel::RELATION_TYPE_STEP_CATEGORY,
                            "step_category_id" => $noteRelation['step_category_id'],
                            "episode_code" => $noteRelation["initial_episode_code"],
                            "entity_id" => $noteRelation["entity_id"],
                            "project_id" => $note["project_id"],
                            "relate_module_code" => $note["module_code"]
                        ]
                    ];

                    $task = [
                        'step_category_id' => $noteRelation['step_category_id']
                    ];
                }

                $result = NoteService::getInstance()->createNote($data);
                if (!empty($result) && !empty($note['review_feedback_id']) && $resultWanted) {
                    $result = $this->dealAddAssetRepairFeedbackNoteRes($task, $entity, $note['review_feedback_id'], $noteRelation['type']);
                }
            }

            if (!empty($note["review_feedback_id"])) {
                //处理审核反馈为进行中
                $reviewFeedbackId = $data["note"]["review_feedback_id"];
                $this->dealReviewFeedbackInProgress($result, $reviewFeedbackId);
            }
        }
        return $result;
    }

    /**
     * 批量修改反馈
     * @param $data
     * @return int
     * @throws Exception
     */
    public function batchUpdateSameNote($data)
    {
        $note = model(NoteModel::class)->where(["id" => ["IN", $data["id"]]])->select();
        if (isset($data["accept_changes"])) {
            $data["demand_status"] = $data["accept_changes"];
            unset($data["accept_changes"]);
        }

        $reviewFeedbackId = $note[0]['review_feedback_id'];
        //权限验证
        $this->findReviewFeedbackPermissionsDeal($reviewFeedbackId, "confirm_review_feedback_note", function () use ($data) {
            return isset($data["demand_status"]);
        });

        //验证判定 (如果有返修中和返修完成的,将不能改变状态)
        foreach ($note as $noteItem) {
            if (isset($data["demand_status"]) && in_array($noteItem['demand_status'], NoteModel::REPAIRED_DEMAND_STATUS) && $data["demand_status"] != $noteItem['demand_status']) {
                throw new LogicException('note repair has been initiated', ErrorCode::NOTE_REPAIR_HAS_BEEN_INITIATED);
            }
        }

        if (isset($data["demand_status"]) && $data["demand_status"] === NoteModel::DEMAND_STATUS_PENDING) {
            $data['confirm_user_id'] = request()->getUserId();
        }

        $instance = CommonService::instance("note");
        $instance->setCurrentModuleCode("note");
        $resData = $instance->commonBatchUpdateSame($data, module_by_code("note"));
        return $resData;
    }

    /**
     * 获取实体下任务列表
     * @param $entityId
     * @return array
     */
    public function getEntityTaskList($entityId)
    {
        $entity = model(EntityModel::class)->field("id as entity_id,initial_episode_code")->find($entityId);
        $taskList = model(TaskModel::class)->where([
            'entity_id' => $entityId,
            'step_type' => 'step',
            '_string' => "JSON_UNQUOTE(JSON_EXTRACT(json,'$.is_dismantle_task')) is null"
        ])
            ->field("id,name,step_category_id,step_id,JSON_UNQUOTE(JSON_EXTRACT(json,'$.task_status')) as task_status")
            ->select();

        if (empty($taskList)) {
            return [];
        }

        $statusService = StatusService::getInstance();
        $notStartedStatus = $statusService->getNotStartedStatus();
        $notStartedStatus = array_column($notStartedStatus, 'id');

        $stepCategoryIds = [];
        foreach ($taskList as $taskIndex => $taskItem) {
            if (in_array($taskItem['task_status'], $notStartedStatus)) {
                unset($taskList[$taskIndex]);
                continue;
            }
            $stepCategoryIds[$taskItem["step_category_id"]] = $taskItem["step_category_id"];
        }

        if (empty($taskList)) {
            return [];
        }

        $stepCategoryIds = array_values($stepCategoryIds);
        $stepCategoryList = model(StepCategoryModel::class)->where([
            'id' => ["IN", $stepCategoryIds]
        ])->field("id,name")->select();

        $stepCategoryList = array_column($stepCategoryList, "name", "id");

        $stepCategoryGroup = [];
        foreach ($taskList as $taskItem) {
            $taskItem['relation_type'] = 'step';
            $taskItem = array_merge($taskItem, $entity);
            if (empty($stepCategoryGroup[$taskItem["step_category_id"]])) {
                $stepCategoryGroup[$taskItem["step_category_id"]] = array_merge([
                    "id" => $taskItem["step_category_id"],
                    "name" => $stepCategoryList[$taskItem["step_category_id"]],
                    'relation_type' => 'step_category',
                    "children" => [$taskItem]], $entity);
            } else {
                $stepCategoryGroup[$taskItem["step_category_id"]]["children"][] = $taskItem;
            }
        }
        return array_values($stepCategoryGroup);
    }

    /**
     * 删除审核反馈返修意见
     * @param $filter
     * @return array
     * @throws Throwable
     */
    public function deleteRepairFeedbackNote($filter)
    {
        //判定是否未发起返修
        $noteModel = model(NoteModel::class);
        $noteList = $noteModel->where($filter)->field("id,demand_status,review_feedback_id")->select();

        if (empty($noteList)) {
            return [];
        }

        //权限判定
        $this->findReviewFeedbackPermissionsDeal($noteList[0]['review_feedback_id'], "delete_feedback_note");

        $noteIds = [];
        foreach ($noteList as $noteItem) {
            if (in_array($noteItem['demand_status'], NoteModel::REPAIRED_DEMAND_STATUS)) {
                throw new LogicException('note repair has been initiated', ErrorCode::NOTE_REPAIR_HAS_BEEN_INITIATED);
            }
            $noteIds[] = $noteItem["id"];
        }

        if (!empty($noteIds)) {
            // 相应的关联关系需要删除
            $noteEntityRelationList = model(NoteEntityRelationModel::class)->where(['note_id' => ["IN", $noteIds]])->select();
            $noteEntityRelationIds = array_column($noteEntityRelationList, 'id');
            // 相应的影响镜头需要删除
            if (!empty($noteEntityRelationIds)) {
                $relationNoteEntityRelationList = model(RelationNoteEntityRelationModel::class)
                    ->where(['note_entity_relation_id' => ["IN", $noteEntityRelationIds]])
                    ->field("id")->select();
                $relationNoteEntityRelationIds = array_column($relationNoteEntityRelationList, 'id');
            }
        }

        $res = [];
        $noteModel->startTrans();
        try {
            if (!empty($relationNoteEntityRelationIds)) {
                $res['relation_note_entity_relation'] = model(RelationNoteEntityRelationModel::class)->where(['id' => ["IN", $relationNoteEntityRelationIds]])->delete();
            }
            if (!empty($noteEntityRelationIds)) {
                $res['note_entity_relation'] = model(NoteEntityRelationModel::class)->where(['id' => ["IN", $noteEntityRelationIds]])->delete();
            }
            if (!empty($noteIds)) {
                $res['note'] = model(NoteModel::class)->where(['id' => ['IN', $noteIds]])->delete();
            }
            $noteModel->commit();
            return $res;
        } catch (Throwable $e) {
            $noteModel->rollback();
            throw $e;
        }
    }

    /**
     * 处理审核反馈状态
     * @param $reviewFeedbackIds
     * @return void
     * @throws \Exception
     */
    public function dealReviewFeedbackFinalStatus($reviewFeedbackIds)
    {
        if (empty($reviewFeedbackIds)) {
            return;
        }

        //如果还存在返修意见没有完成的 需要过滤相应的审核反馈
        $notFinalDemandStatus = NoteModel::NOT_REPAIRED_DEMAND_STATUS;
        $notFinalDemandStatus[] = NoteModel::DEMAND_STATUS_PROCESSING;

        $noFinalReviewFeedbackIds = model(NoteModel::class)->forceMasterDB()
            ->where(["review_feedback_id" => ["IN", $reviewFeedbackIds], "demand_status" => ["IN", $notFinalDemandStatus]])
            ->field("review_feedback_id")
            ->group("review_feedback_id")
            ->select();
        $noFinalReviewFeedbackIds = array_column($noFinalReviewFeedbackIds, "review_feedback_id");
        $reviewFeedbackIds = array_diff($reviewFeedbackIds, $noFinalReviewFeedbackIds);
        if (empty($reviewFeedbackIds)) {
            return;
        }

        model(ReviewFeedbackModel::class)->where(["id" => ['IN', $reviewFeedbackIds]])->save(['status' => ReviewFeedbackModel::STATUS_FINAL]);
    }

    /**
     * 获取审核反馈的媒体列表
     * @param $param
     * @return array
     */
    public function getReviewFeedbackMediaList($param)
    {
        $filter = $param['filter'];
        $page = $param['page'] ?? [1, C('default_page_total')];
        $reviewFeedback = model(ReviewFeedbackModel::class)
            ->field("JSON_UNQUOTE(JSON_EXTRACT(json,'$.review_feedback_cos_media')) as review_feedback_cos_media")
            ->find($filter['id']);

        if (empty($reviewFeedback['review_feedback_cos_media'])) {
            return [];
        }

        $cosMediaList = model(CosMediaModel::class)->where([
            'id' => ["IN", $reviewFeedback['review_feedback_cos_media']]
        ])->page(...$page)->select();

        $createdBy = [];
        foreach ($cosMediaList as $cosMediaIndex => $cosMediaItem) {
            $cosMediaItem['param'] = json_decode($cosMediaItem['param'], true);
            $cosMediaDataList[$cosMediaItem["id"]] = $cosMediaItem;
            if (!in_array($cosMediaItem["created_by"], $createdBy)) {
                $createdBy[] = $cosMediaItem["created_by"];
            }
            $cosMediaList[$cosMediaIndex] = $cosMediaItem;

        }

        //查询创建人
        $createdByMap = [];
        if (!empty($createdBy)) {
            $createdData = model(UserModel::class)->where(["id" => ["IN", $createdBy]])->field("id,name")->select();
            $createdByMap = array_column($createdData, null, "id");
        }

        return array_map(function ($cosMediaItem) use ($createdByMap) {
            $cosMediaItem['created_by'] = $createdByMap[$cosMediaItem['created_by']] ?? [];
            return $cosMediaItem;
        }, $cosMediaList);
    }

    /**
     * 添加镜头反馈
     * @param $res
     * @param $shotData
     * @param int $limit
     * @return array
     * @throws Throwable
     */
    public function addShotRepairFeedbackNote($res, $shotData, $limit = 50)
    {
        $noteService = NoteService::getInstance();
        $shotIds = [];
        for ($start = 0; $start < $limit; $start++) {
            $resDataItem = array_shift($res);
            if (!empty($resDataItem)) {
                $shotIds[] = intval($shotData[$resDataItem['note_entity_relation'][0]['entity_id']]['id']);
                $noteService->createNote($resDataItem);
            }
        }

        foreach ($res as $resItem) {
            Client::send('create_note', build_queue_data_with_xu($resItem));
        }
        return $shotIds;
    }

    /**
     * 筛选里是否存在剧集或者名字筛选
     * @param $filter
     * @param $isExistEpisode
     */
    public function isExistEpisodeFilter($filter, &$isExistEpisode)
    {
        foreach ($filter as $key => $value) {
            if (empty($isExistEpisode) && ($key === "entity.initial_episode_code" || $key === "entity.name" || $key === "entity.id")) {
                $isExistEpisode = true;
            } elseif (is_array($value)) {
                $this->isExistEpisodeFilter($value, $isExistEpisode);
            }
        }
    }

    /**
     * 处理审核反馈为进行中
     * @param $result
     * @param $reviewFeedbackId
     * @throws \Exception
     */
    public function dealReviewFeedbackInProgress($result, $reviewFeedbackId)
    {
        //如果添加成功,且审核反馈状态为已完成需要修改为进行中状态
        if (!empty($result)) {
            $reviewFeedback = model(ReviewFeedbackModel::class)->where(['id' => $reviewFeedbackId, 'status' => ReviewFeedbackModel::STATUS_FINAL])->find();
            if (!empty($reviewFeedback)) {
                model(ReviewFeedbackModel::class)->modifyItem(['id' => $reviewFeedbackId, 'status' => ReviewFeedbackModel::STATUS_IN_PROGRESS]);
            }
        }
    }

    /**
     * 获取资产审核反馈结果
     * @param $task
     * @param $entity
     * @param $reviewFeedbackId
     * @return array
     */
    public function getAssetReviewFeedbackResult($task, $entity, $reviewFeedbackId)
    {

        $task = array_merge($task, $entity);

        $noteFields = [
            "note_entity_relation.id", "note_entity_relation.note_id", "note.text", "note.mark_modify", "note.demand_status",
            "note.demand_status as accept_changes", "note.demand_status as repair_progress",
            "JSON_UNQUOTE(JSON_EXTRACT(note.json, '$.note_cos_media')) as cos_media",
            "JSON_UNQUOTE(JSON_EXTRACT(note.json, '$.reason_type')) as reason_type",
            "note_entity_relation.task_id",
        ];

        $noteEntityRelationEntityId = $entity['id'];
        if (!empty($task['json'])) {
            $taskJson = json_decode($task['json'], true);
            $moduleCodeCategory = module_by_id($task['module_id'])['code'] . "_category";

            if (!empty($taskJson[$moduleCodeCategory])) {
                $task["category"] = $taskJson[$moduleCodeCategory];
            }

            $designId = module_by_id($task['module_id'])['code'] . "_design";

            if (!empty($taskJson[$designId])) {
                $noteEntityRelationEntityId = $taskJson[$designId];
            }

            unset($task['json']);
        }

        if (isset($task['task_id'])) {
            $noteEntityRelationWhere = [
                'note_entity_relation.review_feedback_id' => $reviewFeedbackId,
                "note_entity_relation.entity_id" => $noteEntityRelationEntityId,
                "note_entity_relation.relation_type" => NoteEntityRelationModel::RELATION_TYPE_STEP,
                "note_entity_relation.task_id" => $task['task_id'],
            ];
        } else {
            $noteEntityRelationWhere = [
                'note_entity_relation.review_feedback_id' => $reviewFeedbackId,
                "note_entity_relation.entity_id" => $noteEntityRelationEntityId,
                "note_entity_relation.relation_type" => NoteEntityRelationModel::RELATION_TYPE_STEP_CATEGORY,
                "note_entity_relation.step_category_id" => $task['step_category_id']
            ];
        }

        $noteEntityRelationList = model(NoteEntityRelationModel::class)
            ->join("note on note.id = note_entity_relation.note_id")
            ->where($noteEntityRelationWhere)
            ->field($noteFields)
            ->select();

        if (!empty($noteEntityRelationList)) {
            $task['repair_demand'] = $noteEntityRelationList;

            $noteEntityRelationIds = array_column($noteEntityRelationList, 'id');

            $shotIds = model(RelationNoteEntityRelationModel::class)->where([
                'note_entity_relation_id' => ["IN", $noteEntityRelationIds]
            ])->field("entity_id")->group("entity_id")->select();

            $task['affected_shot_count'] = count($shotIds);
        }

        $stepCategory = model(StepCategoryModel::class)->field("id,name")->find($task['step_category_id']);
        if (!empty($stepCategory)) {
            $task['step_category_name'] = $stepCategory['name'];
        }

        return $task;
    }

    /**
     * 获取审核反馈资产列表
     * @param $param
     * @return array|false|mixed|string
     */
    public function getReviewFeedbackAssetList($param)
    {
        $filter = $param['filter'];
        $page = $param["page"] ?? [1, C('default_page_total')];
        $fields = $param['fields'] ?? "id,name";

        $projectId = $filter['project_id'];
        $initialEpisodeCode = $filter['initial_episode_code'];
        unset($filter['project_id'], $filter['initial_episode_code']);

        $episode = model(EntityModel::class)->where([
            'project_id' => $projectId,
            'code' => $initialEpisodeCode,
            'module_id' => module_by_code("episode")['id']
        ])->field("id")->select();

        if (empty($episode)) {
            return [];
        }
        //显示字段
        $fields = "epr.entity_id as id,name,initial_episode_code,entity.showings_number,entity.json";
        $fields .= ",JSON_UNQUOTE(JSON_EXTRACT(entity.json, '$.asset_grade')) as asset_grade";
        $fields .= ",JSON_UNQUOTE(JSON_EXTRACT(entity.json, '$.asset_category')) as asset_category";

        $episodeIds = array_column($episode, 'id');
        $returnList = model(EntityEpisodeRelationModel::class)->alias('epr')
            ->join("entity on entity.id = epr.entity_id")
            ->where([
                'epr.project_id' => $projectId,
                'epr.episode_id' => ["IN", $episodeIds],
                'epr.entity_module_id' => ["IN", [module_by_code("asset")['id'], module_by_code("level")['id']]],
                'entity.module_id' => ["IN", [module_by_code("asset")['id'], module_by_code("level")['id']]]
            ])
            ->where($filter)
            ->field($fields)
            ->group("epr.entity_id")
            ->page(...$page)
            ->select();
        foreach ($returnList as &$val) {
            $val['json'] = json_decode($val['json'], true);
        }
        return $returnList;
    }

    /**
     * 获取审核反馈返修意见详情
     * @param $noteId
     * @return array
     */
    public function getReviewFeedbackNoteDetail($noteId)
    {
        $fields = ["id", "text", "review_feedback_id", "proposer", "proposer_department",
            "JSON_UNQUOTE(JSON_EXTRACT(note.json, '$.note_cos_media')) as cos_media",
            "JSON_UNQUOTE(JSON_EXTRACT(note.json, '$.reason_type')) as reason_type",
        ];
        $result = [];
        $note = model(NoteModel::class)->field($fields)->find($noteId);

        $result['note'] = $note;
        // 镜头,环节
        $noteEntityRelation = model(NoteEntityRelationModel::class)->where([
            'note_id' => $note['id']
        ])->field([
            "id", "relation_type", "note_id", "entity_id", "step_category_id", "step_id", "relate_module_code"
        ])->find();

        if (empty($noteEntityRelation)) {
            return [];
        }

        if (!empty($note['review_feedback_id'])) {
            $reviewFeedback = model(ReviewFeedbackModel::class)->field("id,demand_type")->find($note['review_feedback_id']);
        }

        // 资产
        if ($noteEntityRelation['relate_module_code'] == "asset") {
            $entity = model(EntityModel::class)->field("id,initial_episode_code,showings_number,project_id,name,code")->find($noteEntityRelation['entity_id']);
            if (empty($reviewFeedback["demand_type"]) || $reviewFeedback["demand_type"] != ReviewFeedbackModel::DEMAND_TYPE_DEMAND_ORDER) {
                $entity = model(EntityModel::class)->where([
                    'project_id' => $entity["project_id"],
                    "module_id" => ["IN", [module_by_code("asset")['id'], module_by_code("level")['id']]],
                    "code" => $entity["code"]
                ])->field("id,initial_episode_code,showings_number,name")->find();
            }


        } else {
            $entity = model(EntityModel::class)->field("id,initial_episode_code,showings_number,name")->find($noteEntityRelation['entity_id']);
        }

        $result['entity'] = $entity;

        if ($noteEntityRelation['relation_type'] == NoteEntityRelationModel::RELATION_TYPE_STEP) {
            $result['step'] = model(StepModel::class)->field("id,name,step_category_id")->find($noteEntityRelation['step_id']);
        }

        $result['step_category'] = model(StepCategoryModel::class)->field("id,name")->find($noteEntityRelation['step_category_id']);

        //内容,附件
        if (!empty($note['cos_media'])) {
            $cosMedia = explode(",", $note['cos_media']);
            $cosMediaList = model(CosMediaModel::class)->where(['id' => ["IN", $cosMedia]])->select();
            $cosMediaList = array_map(function ($cosMediaItem) {
                $cosMediaItem['param'] = json_decode($cosMediaItem['param']);
                return $cosMediaItem;
            }, $cosMediaList);
            $result['cos_media'] = $cosMediaList;
        }
        // 影响镜头
        if ($noteEntityRelation["relate_module_code"] == 'asset') {

            $result['affected_entity_relation_list'] = [];

            $relationNoteEntityRelationList = model(RelationNoteEntityRelationModel::class)
                ->where(["note_entity_relation_id" => $noteEntityRelation['id']])
                ->select();

            if (!empty($relationNoteEntityRelationList)) {
                $entityIds = array_unique(array_column($relationNoteEntityRelationList, "entity_id"));
                $entityList = model(EntityModel::class)->where(["id" => ["IN", $entityIds]])->field("id,project_id,initial_episode_code,code,name,showings_number")->order("initial_episode_code asc")->select();

                $entityWhere = [];
                $projectId = 0;
                foreach ($entityList as $entityListItem) {
                    if (empty($projectId)) {
                        $projectId = $entityListItem['project_id'];
                    }
                    $entityWhere[] = [
                        'initial_episode_code' => $entityListItem['initial_episode_code'],
                        'showings_number' => $entityListItem['showings_number'],
                        'code' => $entityListItem['code'],
                    ];
                }

                $entityWhere["_logic"] = "OR";
                $storyboardList = model(EntityModel::class)
                    ->where(['project_id' => $projectId, 'module_id' => module_by_code("shot")['id'], $entityWhere])
                    ->field("id")
                    ->select();


                $result['affected_entity_relation_list'] = array_column($storyboardList, 'id');
            }


        }
        return $result;
    }

    /**
     * 返回影响镜头的数据
     * @param $noteEntityRelationIds
     * @return array
     */
    public function getAffectedShotData($noteEntityRelationIds)
    {
        $relationNoteEntityRelationList = model(RelationNoteEntityRelationModel::class)
            ->where(["note_entity_relation_id" => $noteEntityRelationIds])
            ->select();

        if (empty($relationNoteEntityRelationList)) {
            return [];
        }

        $entityIds = array_unique(array_column($relationNoteEntityRelationList, "entity_id"));
        $entityList = model(EntityModel::class)->where(["id" => ["IN", $entityIds]])->field("id,project_id,initial_episode_code,code,name,showings_number")->order("initial_episode_code asc")->select();

        $entityWhere = [];
        $projectId = 0;
        foreach ($entityList as $entityListItem) {
            if (empty($projectId)) {
                $projectId = $entityListItem['project_id'];
            }
            $entityWhere[] = [
                'initial_episode_code' => $entityListItem['initial_episode_code'],
                'showings_number' => $entityListItem['showings_number'],
                'code' => $entityListItem['code'],
            ];
        }

        $entityWhere["_logic"] = "OR";
        $storyboardList = model(EntityModel::class)
            ->where(['project_id' => $projectId, 'module_id' => module_by_code("shot")['id'], $entityWhere])
            ->field("id,initial_episode_code,code,name,showings_number")
            ->select();

        $entityGroup = [];
        foreach ($storyboardList as $entityItem) {
            if (!isset($entityGroup[$entityItem["initial_episode_code"]])) {
                $entityGroup[$entityItem["initial_episode_code"]] = [
                    'id' => $entityItem["initial_episode_code"],
                    'name' => $entityItem["initial_episode_code"],
                    'children' => [$entityItem]
                ];
            } else {
                $entityGroup[$entityItem["initial_episode_code"]]['children'][] = $entityItem;
            }
        }

        return array_values(array_map(function ($entityGroupItem) {
            $entityGroupItem['count'] = count($entityGroupItem['children']);
            return $entityGroupItem;
        }, $entityGroup));
    }

    /**
     * 更新审核反馈
     * @param $data
     * @return array
     * @throws \Exception
     * @throws Throwable
     */
    public function updateRepairFeedbackNote($data)
    {
        // 目前暂时只是更改内容
        $relationIds = [];

        $resData = [];

        //获取关联id
        $noteEntityRelation = model(NoteEntityRelationModel::class)->where([
            'note_id' => $data['note']['id']
        ])->field("id,entity_id,step_category_id,relation_type,task_id,review_feedback_id")->find();

        // 验证权限
        $this->findReviewFeedbackPermissionsDeal($noteEntityRelation['review_feedback_id'], "confirm_review_feedback_note", function () use ($data) {
            return isset($data['note']['demand_status']);
        });

        $relationNoteEntityRelationModel = model(RelationNoteEntityRelationModel::class);
        $relationNoteEntityRelationModel->startTrans();

        $currentModuleLinkRelation = model(NoteModel::class)->parserFilterModuleRelation(true);
        try {
            foreach ($data as $moduleCode => $item) {
                if (isset($currentModuleLinkRelation[$moduleCode]) && $currentModuleLinkRelation[$moduleCode]['module_code'] === 'cos_media') {
                    $mediaIds = [];
                    if (is_many_dimension_array($item)) {
                        foreach ($item as $mediaItem) {
                            if (empty($mediaItem)) {
                                continue;
                            }

                            // 判定是否需要更新
                            if (!empty($mediaItem["id"])) {
                                $mediaIds[] = $mediaItem["id"];
                                if (!empty($mediaItem['param'])) {
                                    CosMediaService::getInstance()->findAddMedia($mediaItem);
                                }
                            } else {
                                $mediaData = CosMediaService::getInstance()->findAddMedia($mediaItem);
                                if (!in_array($mediaData['id'], $mediaIds)) {
                                    $mediaIds[] = $mediaData['id'];
                                }
                            }
                        }
                    }
                    $relationIds[$moduleCode] = join(',', $mediaIds);
                }
            }

            // 把水平自定义关联数据回插入主表
            foreach ($relationIds as $filed => $relationId) {
                $data['note'][$filed] = $relationId;
            }

            $noteModule = module_by_code("note");
            $commonService = CommonService::instance("note");
            $resData['note'] = $commonService->commonUpdate(['data' => $data['note']], $noteModule, false);
            // 关联资产、镜头、任务
            if (isset($data["note_relation"])) {
                $data['note_relation']['id'] = $noteEntityRelation['id'];
                $data['note_relation'] = NoteService::getInstance()->transformNoteEntityRelationList([$data['note_relation']])[0];
                model(NoteEntityRelationModel::class)->modifyItem($data['note_relation']);
            }
            //受影响的镜头
            if (isset($data["affected_entity_relation"])) {

                if (empty($noteEntityRelation)) {
                    return [];
                }

                $relationNoteEntityRelationList = $relationNoteEntityRelationModel->where([
                    'note_entity_relation_id' => $noteEntityRelation['id']
                ])->field("id,note_entity_relation_id,entity_id")->select();
                $oldEntityIds = array_column($relationNoteEntityRelationList, 'entity_id');

                $affectedEntityRelationList = array_unique($data["affected_entity_relation"]);

                $storyboardIds = [];
                if (!empty($affectedEntityRelationList)) {
                    // 将镜头替换为分镜
                    $shotList = model(EntityModel::class)
                        ->where(['id' => ["IN", $affectedEntityRelationList]])
                        ->field("id,module_id,JSON_UNQUOTE(JSON_EXTRACT(json,'$.shot_storyboard')) as storyboard_id")
                        ->select();

                    foreach ($shotList as $shotItem) {
                        if (module_by_id($shotItem['module_id'])['code'] == 'shot') {
                            $storyboardIds[] = $shotItem["storyboard_id"];
                        } else {
                            $storyboardIds[] = $shotItem['id'];
                        }
                    }
                }

                // 比较出新增的
                $addStoryboardIds = array_diff($storyboardIds, $oldEntityIds);
                // 比较出删除的
                $deleteStoryboardIds = array_diff($oldEntityIds, $storyboardIds);


                //组装数据
                if (!empty($addStoryboardIds)) {
                    $affectedEntityRelationData = [];
                    foreach ($addStoryboardIds as $addStoryboardId) {
                        $affectedEntityRelationData[] = [
                            "note_entity_relation_id" => $noteEntityRelation["id"],
                            "entity_id" => $addStoryboardId,
                            "created" => time()
                        ];
                    }
                    if (!empty($affectedEntityRelationData)) {
                        $resData['add_affected_shot'] = $relationNoteEntityRelationModel->addAll($affectedEntityRelationData);
                    }
                }

                if (!empty($deleteStoryboardIds)) {
                    $relationNoteEntityRelationIds = [];
                    foreach ($relationNoteEntityRelationList as $relationNoteEntityRelationItem) {
                        if (in_array($relationNoteEntityRelationItem['entity_id'], $deleteStoryboardIds)) {
                            $relationNoteEntityRelationIds[] = $relationNoteEntityRelationItem['id'];
                        }
                    }
                    $resData['delete_affected_shot'] = $relationNoteEntityRelationModel->where(['id' => ["IN", $relationNoteEntityRelationIds]])->delete();
                }
            }
            $relationNoteEntityRelationModel->commit();

            if ($resData['note']["module_code"] == 'asset') {
                $design = model(EntityModel::class)
                    ->field("id,name,initial_episode_code,showings_number,module_id,json,id as entity_id,project_id")
                    ->find($noteEntityRelation['entity_id']);

                if ($noteEntityRelation['relation_type'] === NoteEntityRelationModel::RELATION_TYPE_STEP) {
                    $task = model(TaskModel::class)->field("id as task_id,step_category_id,name as task_name")->find($noteEntityRelation['task_id']);
                } else {
                    $task = [
                        'step_category_id' => $noteEntityRelation['step_category_id']
                    ];
                }

                return $this->dealAddAssetRepairFeedbackNoteRes($task, $design, $resData['note']['review_feedback_id'], $noteEntityRelation['relation_type']);


            } else {
                return [$noteEntityRelation['entity_id']];
            }


        } catch (Throwable $e) {
            $relationNoteEntityRelationModel->rollback();
            throw  $e;
        }
    }

    /**
     * 处理添加资产返修反馈结果
     * @param $task
     * @param $entity
     * @param $reviewFeedbackId
     * @param $type
     * @return array
     */
    public function dealAddAssetRepairFeedbackNoteRes($task, $entity, $reviewFeedbackId, $type)
    {
        $result = $this->getAssetReviewFeedbackResult($task, $entity, $reviewFeedbackId);
        if (!empty($entity['json'])) {
            $entityJson = json_decode($entity['json'], true);
            $result['entity_id'] = $entityJson[module_by_id($entity['module_id'])['code'] . "_design"] ?? $result['id'];
        }

        $result['relation_type'] = $type;

        $result['key'] = join("_", [$result["relation_type"], $result["entity_id"], $result["step_category_id"], $result["task_id"] ?? 0]);
        return $result;
    }


    /**
     * 获取分类统计
     * @param $reviewFeedbackId
     * @param $episodeCode
     * @param $projectId
     * @param $stepCategoryIds
     * @return array[]
     */
    public function getStepCategoryCount($reviewFeedbackId, $episodeCode, $projectId, $stepCategoryIds)
    {
        // 类型个数
        $resultList = model(NoteEntityRelationModel::class)
            ->join('note on note.id = note_entity_relation.note_id')
            ->join("entity on entity.id = note_entity_relation.entity_id")
            ->where([
                "note_entity_relation.review_feedback_id" => $reviewFeedbackId,
                "note_entity_relation.relate_module_code" => 'shot',
                "note.review_feedback_id" => $reviewFeedbackId,
                "note.module_code" => 'shot',
                "entity.initial_episode_code" => $episodeCode
            ])
            ->field("note_entity_relation.entity_id,note_entity_relation.step_category_id,JSON_UNQUOTE(JSON_EXTRACT(note.json, '$.reason_type')) as reason_type")
            ->group("note_entity_relation.entity_id,note_entity_relation.step_category_id,JSON_UNQUOTE(JSON_EXTRACT(note.json, '$.reason_type'))")
            ->select();

        $resultCount = [];
        foreach ($resultList as $resultItem) {
            if (!isset($resultCount[$resultItem['step_category_id']][$resultItem['reason_type']])) {
                $resultCount[$resultItem['step_category_id']][$resultItem['reason_type']] = 1;
            } else {
                $resultCount[$resultItem['step_category_id']][$resultItem['reason_type']] += 1;
            }
        }

        //必改
        $markModifyList = model(NoteEntityRelationModel::class)
            ->join('note on note.id = note_entity_relation.note_id')
            ->join("entity on entity.id = note_entity_relation.entity_id")
            ->where([
                "note_entity_relation.review_feedback_id" => $reviewFeedbackId,
                "note_entity_relation.relate_module_code" => 'shot',
                "note.review_feedback_id" => $reviewFeedbackId,
                "note.module_code" => 'shot',
                "note.mark_modify" => "yes",
                "entity.initial_episode_code" => $episodeCode
            ])
            ->field("note_entity_relation.entity_id,note_entity_relation.step_category_id,JSON_UNQUOTE(JSON_EXTRACT(note.json, '$.reason_type')) as reason_type")
            ->group("note_entity_relation.entity_id,note_entity_relation.step_category_id,JSON_UNQUOTE(JSON_EXTRACT(note.json, '$.reason_type'))")
            ->select();

        $markModifyCount = [];
        foreach ($markModifyList as $markModifyItem) {
            if (!isset($markModifyCount[$markModifyItem['step_category_id']][$markModifyItem['reason_type']])) {
                $markModifyCount[$markModifyItem['step_category_id']][$markModifyItem['reason_type']] = 1;
            } else {
                $markModifyCount[$markModifyItem['step_category_id']][$markModifyItem['reason_type']] += 1;
            }
        }

        //接受
        $demandStatusList = model(NoteEntityRelationModel::class)
            ->join('note on note.id = note_entity_relation.note_id')
            ->join("entity on entity.id = note_entity_relation.entity_id")
            ->where([
                "note_entity_relation.review_feedback_id" => $reviewFeedbackId,
                "note_entity_relation.relate_module_code" => 'shot',
                "note.review_feedback_id" => $reviewFeedbackId,
                "note.module_code" => 'shot',
                "note.demand_status" => ["IN", ["pending", "processing", "final"]],
                "entity.initial_episode_code" => $episodeCode
            ])
            ->field("note_entity_relation.entity_id,note_entity_relation.step_category_id,JSON_UNQUOTE(JSON_EXTRACT(note.json, '$.reason_type')) as reason_type")
            ->group("note_entity_relation.entity_id,note_entity_relation.step_category_id,JSON_UNQUOTE(JSON_EXTRACT(note.json, '$.reason_type'))")
            ->select();

        $demandStatusCount = [];
        foreach ($demandStatusList as $demandStatusItem) {
            if (!isset($demandStatusCount[$demandStatusItem['step_category_id']][$demandStatusItem['reason_type']])) {
                $demandStatusCount[$demandStatusItem['step_category_id']][$demandStatusItem['reason_type']] = 1;
            } else {
                $demandStatusCount[$demandStatusItem['step_category_id']][$demandStatusItem['reason_type']] += 1;
            }
        }

        //返修了的
        $repairList = model(NoteEntityRelationModel::class)
            ->join('note on note.id = note_entity_relation.note_id')
            ->join("entity on entity.id = note_entity_relation.entity_id")
            ->where([
                "note_entity_relation.review_feedback_id" => $reviewFeedbackId,
                "note_entity_relation.relate_module_code" => 'shot',
                "note.review_feedback_id" => $reviewFeedbackId,
                "note.module_code" => 'shot',
                "note.demand_status" => ["IN", ["processing", "final"]],
                "entity.initial_episode_code" => $episodeCode
            ])
            ->field("note_entity_relation.entity_id,note_entity_relation.step_category_id")
            ->group("note_entity_relation.entity_id,note_entity_relation.step_category_id")
            ->select();

        $repairCount = [];
        foreach ($repairList as $repairItem) {
            if (!isset($repairCount[$repairItem['step_category_id']])) {
                $repairCount[$repairItem['step_category_id']] = 1;
            } else {
                $repairCount[$repairItem['step_category_id']] += 1;
            }
        }


        $statusDataList = array_column(model(StatusModel::class)->where(['code' => ["IN", ["closed", "wait_for_merge_sequence"]]])->select(), 'id');
        $shotCount = model(EntityModel::class)
            ->where([
                'project_id' => $projectId,
                'initial_episode_code' => $episodeCode,
                'module_id' => module_by_code('shot')['id'],
                "JSON_UNQUOTE(JSON_EXTRACT(json, '$.shot_status'))" => ["NOT IN", $statusDataList],
            ])
            ->field("count(id) as count")
            ->find();

        $episode = model(EntityModel::class)->where(['project_id' => $projectId, 'module_id' => module_by_code('episode')['id'], 'code' => $episodeCode])->find();

        $stepCategoryCount = model(TaskModel::class)
            ->join("entity on entity.id = task.entity_id")
            ->where([
                'task.project_id' => $projectId,
                'task.episode_id' => $episode['id'],
                'task.entity_module_id' => module_by_code('shot')['id'],
                "task.step_category_id" => ["IN", $stepCategoryIds],
                "JSON_UNQUOTE(JSON_EXTRACT(task.json, '$.task_status'))" => ["NOT IN", $statusDataList],
                "JSON_UNQUOTE(JSON_EXTRACT(entity.json, '$.shot_status'))" => ["NOT IN", $statusDataList],
            ])
            ->field("task.step_category_id,COUNT(DISTINCT task.entity_id) as count")
            ->group("task.step_category_id")
            ->select();
        $stepCategoryCount = array_column($stepCategoryCount, 'count', 'step_category_id');

        // 关卡 ,资产返修个数
        $assetRepairList = model(NoteEntityRelationModel::class)
            ->join("note on note_entity_relation.note_id = note.id")
            ->join("relation_note_entity_relation on note_entity_relation.id = relation_note_entity_relation.note_entity_relation_id")
            ->join("entity on entity.id = note_entity_relation.entity_id")
            ->join("entity shot on shot.id = relation_note_entity_relation.entity_id")
            ->where([
                "note_entity_relation.review_feedback_id" => $reviewFeedbackId,
                "note_entity_relation.relate_module_code" => 'asset',
                "note.demand_status" => ["IN", ["processing", "final"]],
                "shot.initial_episode_code" => $episodeCode,
            ])
            ->field("entity.id,entity.json")
            ->group("entity.id")
            ->select();


        $assetRepairCount = [
            'level' => 0,
            'asset' => 0,
        ];
        foreach ($assetRepairList as $assetRepairListItem) {
            $assetJson = json_decode($assetRepairListItem['json'], true);
            if ($assetJson["design_category"] == "关卡") {
                ++$assetRepairCount['level'];
            } else {
                ++$assetRepairCount['asset'];
            }
        }

        return [
            'result_count' => $resultCount,
            'mark_modify_count' => $markModifyCount,
            'demand_status_count' => $demandStatusCount,
            'repair_count' => $repairCount,
            'shot_count' => $shotCount['count'],
            'step_category_count' => $stepCategoryCount,
            'asset_repair_count' => $assetRepairCount
        ];
    }

    /**
     * 筛选里是否存在note筛选
     * @param $filter
     * @param $isNote
     * @return array
     */
    public function isExistNoteFilter(&$filter, &$isNote)
    {
        $noteFilter = [];
        //如果同时存在,取其交集
        if (isset($filter['note.accept_changes']) && isset($filter['note.repair_progress'])) {
            $demandStatus = array_intersect($filter['note.accept_changes'], $filter['note.repair_progress']);
            if (!empty($demandStatus)) {
                $isNote = true;
                $noteFilter['note.demand_status'] = ["IN", $demandStatus];
                $filter['note.demand_status'] = ["IN", $demandStatus];
            } else {
                return null;
            }
            unset($filter['note.accept_changes'], $filter['note.repair_progress']);
        }


        foreach ($filter as $key => $value) {
            if (in_array("note", explode(".", $key))) {
                $isNote = true;
                if (in_array($key, ['note.accept_changes', 'note.repair_progress'])) {
                    $noteFilter['note.demand_status'] = ["IN", $value];
                    $filter['note.demand_status'] = ["IN", $value];
                    unset($filter[$key]);
                } elseif ($key == 'note.reason_type') {
                    $noteFilter["JSON_UNQUOTE(JSON_EXTRACT(note.json, '$.reason_type'))"] = $value;
                    $filter["JSON_UNQUOTE(JSON_EXTRACT(note.json, '$.reason_type'))"] = $value;
                    unset($filter[$key]);
                } else {
                    $noteFilter[$key] = $value;
                }
            }
            if (in_array("note_entity_relation", explode(".", $key))) {
                $noteFilter[$key] = $value;
            }
        }
        return $noteFilter;
    }


    /**
     * 获取添加审核反馈时的实体列表
     * @param $entityWhere
     * @param $entityModuleId
     * @param $projectId
     * @param $stepCategoryId
     * @param string $type
     * @return array
     */
    public function getAddRepairFeedbackNoteShotList($entityWhere, $entityModuleId, $projectId, $stepCategoryId, $type = 'review_feedback')
    {
        if (in_array(module_by_code("storyboard")['id'], $entityModuleId)) {
            $shotList = model(EntityModel::class)
                ->where([
                    'project_id' => $projectId,
                    'module_id' => ["IN", $entityModuleId],
                    $entityWhere
                ])
                ->field("id,code,initial_episode_code,showings_number,module_id,JSON_UNQUOTE(JSON_EXTRACT(json,'$.shot_storyboard')) as shot_storyboard")
                ->select();

            if (empty($shotList)) {
                return [];
            }


            if ($type == ReviewFeedbackModel::DEMAND_TYPE_DEMAND_ORDER) {
                return array_column($shotList, null, 'id');
            }

            // 找出没有该工序的镜头任务将其去除掉
            $shotWhere = [];
            $shotFilterIds = [];
            foreach ($shotList as $shotItem) {
                $shotKey = join("_", [$shotItem['initial_episode_code'], $shotItem['showings_number'], $shotItem['code']]);

                if (module_by_id($shotItem['module_id'])['code'] === 'shot') {
                    $shotFilterIds[] = $shotItem['id'];
                    $shotItem['id'] = $shotItem['shot_storyboard'];
                } else {
                    $shotWhere[] = [
                        'entity.initial_episode_code' => $shotItem['initial_episode_code'],
                        'entity.showings_number' => $shotItem['showings_number'],
                        'entity.code' => $shotItem['code'],
                    ];
                }

                $shotStoryboardMap[$shotKey] = $shotItem;
            }
            $shotFilter = [];
            if (!empty($shotFilterIds)) {
                $shotFilter["entity.id"] = ["IN", $shotFilterIds];
            }

            if (!empty($shotWhere)) {
                $shotWhere["_logic"] = "OR";
                $shotWhere = [
                    'entity.project_id' => $projectId,
                    'entity.module_id' => module_by_code("shot")['id'],
                    $shotWhere
                ];

                if (!empty($shotFilter)) {
                    $shotFilter = [
                        $shotFilter,
                        $shotWhere
                    ];
                } else {
                    $shotFilter = $shotWhere;
                }
            }

            $shotData = model(EntityModel::class)
                ->join("task on task.entity_id = entity.id")
                ->where([$shotFilter, 'step_type' => 'step', 'step_category_id' => $stepCategoryId])
                ->field("entity.id,entity.initial_episode_code,entity.showings_number,entity.code,JSON_UNQUOTE(JSON_EXTRACT(entity.json,'$.shot_storyboard')) as shot_storyboard")
                ->group("entity.id")
                ->select();


            $storyboardMap = [];
            foreach ($shotData as $shotDataItem) {
                if (isset($shotStoryboardMap[join("_", [$shotDataItem['initial_episode_code'], $shotDataItem['showings_number'], $shotDataItem['code']])])) {
                    $shotDataItem['id'] = $shotDataItem['shot_storyboard'];
                    $storyboardMap[$shotDataItem['shot_storyboard']] = $shotDataItem;
                }
            }
        } else {
            $shotList = model(EntityModel::class)
                ->join("task on task.entity_id = entity.id")
                ->where([
                    'entity.project_id' => $projectId,
                    'entity.module_id' => ["IN", $entityModuleId],
                    $entityWhere,
                    "task.step_type" => TaskModel::STEP_TYPE_STEP,
                    "task.step_category_id" => $stepCategoryId
                ])
                ->field("entity.id,entity.initial_episode_code,entity.module_id,JSON_UNQUOTE(JSON_EXTRACT(entity.json,'$.shot_storyboard')) as shot_storyboard")
                ->select();

            $storyboardMap = [];
            foreach ($shotList as $shotItem) {
                $shotItem['id'] = $shotItem['shot_storyboard'];
                $storyboardMap[$shotItem['shot_storyboard']] = $shotItem;
            }
        }

        return $storyboardMap;
    }

    /**
     * 发送审核反馈确认通知
     * @param int $reviewFeedbackId 审核反馈id
     * @param array $notifyUserIdList 通知用户unionId
     * @param int $confirmLimitTime 确认限制时间
     * @param string $notifyContent 通知内容
     * @param int $currentUserUnionId 当前用户unionId
     * @return array
     * @throws LogicException
     */
    public function sendNotify(int $reviewFeedbackId, array $notifyUserIdList, int $confirmLimitTime, string $notifyContent, int $currentUserUnionId)
    {
        $reviewFeedback = model(ReviewFeedbackModel::class)->find($reviewFeedbackId);
        if (empty($reviewFeedback)) {
            throw new LogicException('review feedback not found', ErrorCode::REVIEW_FEEDBACK_NOT_FOUND);
        }
        $project = model(ProjectModel::class)->find($reviewFeedback['project_id']);
        if (empty($project)) {
            throw new LogicException('project not found', ErrorCode::PROJECT_NOT_FOUND);
        }
        $res = [
            'notify_count' => 0,
            'review_feedback' => null,
        ];
        if ($confirmLimitTime != $reviewFeedback['confirm_limit_time']) {
            $res['review_feedback'] = model(ReviewFeedbackModel::class)->modifyItem(['id' => $reviewFeedbackId, 'confirm_limit_time' => $confirmLimitTime]);
            if ($res['review_feedback'] !== false && $confirmLimitTime > time()) {
                Client::send('change_review_feedback_permission', build_queue_data_with_xu([
                    'change_time' => $confirmLimitTime,
                    'data_field' => 'confirm_limit_time',
                    'review_feedback_id' => $reviewFeedbackId,
                    'change_to' => 'off',
                ]), $confirmLimitTime - time());
            }
        }
        $notifyUserIdList = array_filter($notifyUserIdList, function ($i) use ($currentUserUnionId) {
            return $i != $currentUserUnionId;
        });

        $notifyMessageData = [
            'message' => [
                'content' => [
                    "data" => [
                        'review_feedback_id' => $reviewFeedbackId,
                        'project_name' => $project['name'],
                        'project_id' => $reviewFeedback['project_id'],
                        'title' => $reviewFeedback['title'],
                        'content' => $notifyContent,
                        'start_time' => time(),
                        'end_time' => $confirmLimitTime
                    ],
                    'type' => $reviewFeedback['demand_type'] === ReviewFeedbackModel::DEMAND_TYPE_REVIEW_FEEDBACK ? 'review_feedback_confirm_notify' : 'demand_order_confirm_notify',
                ],
                'content_type' => 'card_message_v2',
            ],
            'one2one' => array_values($notifyUserIdList),
        ];
        $receiverIdList = model(UserModel::class)
            ->where(['union_id' => ['in', $notifyUserIdList]])
            ->field('id as user_id')
            ->select();
        $hookData = [
            'demand_order_id' => $reviewFeedbackId,
//            'step_category_id' => array_filter(array_column($showSecondLevelStepCategoryList, 'id')),
            'receiver' => $receiverIdList,
            'end_time' => $confirmLimitTime,
            'notice_all' => true
        ];
        Hook::listen('demand_order_require_confirm', $hookData);

        $res['chat_list'] = teamones_request('teamones-im', 'message/batch_send_message', ['data' => $notifyMessageData])['data']['chat_list'];

        return $res;
    }

    /**
     * 权限处理
     * @param      $reviewFeedbackId
     * @param      $filter
     * @param null $dealFun
     */
    public function findReviewFeedbackPermissionsDeal($reviewFeedbackId, $filter, $dealFun = null)
    {
        if (empty($reviewFeedbackId)) {
            return;
        }
        $reviewFeedback = model(ReviewFeedbackModel::class)->find($reviewFeedbackId);
        if ($reviewFeedback['demand_type'] !== ReviewFeedbackModel::DEMAND_TYPE_REVIEW_FEEDBACK) {
            return;
        }
        $currentPermissions = $reviewFeedback['permissions'];
        $this->reviewFeedbackPermissionsDeal($currentPermissions, $filter, $dealFun);
    }

    /**
     * 权限处理
     * @param               $currentPermissions
     * @param               $filter
     * @param callable|null $dealFun
     */
    public function reviewFeedbackPermissionsDeal($currentPermissions, $filter, callable $dealFun = null)
    {
        if ($currentPermissions == ReviewFeedbackModel::PERMISSIONS_OFF) {
            $this->reviewFeedbackPermissionsCompare($currentPermissions, []);
        }

        //外部条件,如有条件则不通过
        if (isset($dealFun) && !$dealFun()) {
            return;
        }

        switch ($filter) {
            case "delete_feedback_note":
            case "entry_review_feedback_note": //录入审核反馈
                $this->reviewFeedbackPermissionsCompare($currentPermissions, [ReviewFeedbackModel::PERMISSIONS_ON, ReviewFeedbackModel::PERMISSIONS_ONE]);
                break;
            case "send_repair":
            case "confirm_review_feedback_note": //确认审核反馈
                $this->reviewFeedbackPermissionsCompare($currentPermissions, [ReviewFeedbackModel::PERMISSIONS_ON, ReviewFeedbackModel::PERMISSIONS_TWO]);
                break;
        }
    }

    /**
     * 审核反馈权限比较
     * @param $currentPermissions
     * @param $needPermissionsList
     */
    public function reviewFeedbackPermissionsCompare($currentPermissions, $needPermissionsList)
    {
        if (!in_array($currentPermissions, $needPermissionsList)) {
            throw new ReviewFeedbackPermissionException("review feedback permissions insufficient", ErrorCode::REVIEW_FEEDBACK_PERMISSIONS_INSUFFICIENT);
        }
    }

    /**
     * 导入审核反馈
     * @param $data
     * @param $moduleCode
     * @param $projectId
     * @param $reviewFeedbackId
     * @return array
     * @throws \Throwable
     * @throws \think\Exception
     */
    public function importRepairFeedbackNote($data, $moduleCode, $projectId, $reviewFeedbackId)
    {
        $reviewFeedback = model(ReviewFeedbackModel::class)->find($reviewFeedbackId);
        $this->reviewFeedbackPermissionsDeal($reviewFeedback['permissions'], "entry_review_feedback_note");

        if ($moduleCode == 'shot') {
            $addData = $this->importShotRepairFeedbackNote($data, $projectId, $reviewFeedback);
        } else {
            $addData = $this->importAssetRepairFeedbackNote($data, $projectId, $reviewFeedback);
        }

        $result = [];
        foreach ($addData as $addItem) {
            $result[] = $this->addRepairFeedbackNote($addItem);
        }

        return $result;
    }

    /**
     * 导入资产返修反馈意见
     * @param $assetData
     * @param $projectId
     * @param $reviewFeedback
     * @return array
     */
    public function importAssetRepairFeedbackNote($assetData, $projectId, $reviewFeedback)
    {
        $reviewFeedbackId = $reviewFeedback['id'];
        $reviewFeedbackInitialEpisodeCodeList = explode(",", $reviewFeedback['episode_codes']);
        $result = [];
        $assetNameList = [];
        $stepName = [];
        $stepCategoryName = [];
        foreach ($assetData as $assetDataIndex => $assetDataItem) {
            if (!isset($assetDataItem['type'])) {
                $assetDataItem['type'] = 'step_category';
            }

            if ($assetDataItem['type'] == 'step_category') {
                $stepCategoryName[$assetDataItem["step_category_name"]] = $assetDataItem["step_category_name"];
            } else {
                $stepName[$assetDataItem["step_name"]] = $assetDataItem["step_name"];
            }
            $assetNameList[$assetDataItem['name']] = $assetDataItem['name'];
            $assetData[$assetDataIndex] = $assetDataItem;
        }

        $assetNameList = array_values($assetNameList);
        $entityList = model(EntityModel::class)->where([
            'project_id' => $projectId,
            'name' => ["IN", $assetNameList],
            'module_id' => ["IN", [module_by_code("level")['id'], module_by_code("asset")["id"]]]
        ])->field("id,initial_episode_code,name")->select();

        if (empty($entityList)) {
            return [];
        }

        $entityMap = [];
        $initialEpisodeCodeList = [];
        foreach ($entityList as $entityItem) {
            $initialEpisodeCodeList[$entityItem['initial_episode_code']] = $entityItem['initial_episode_code'];
            $entityMap[$entityItem['name']] = $entityItem;
        }

        $initialEpisodeCodeList = array_values($initialEpisodeCodeList);
        if (!empty(array_diff($initialEpisodeCodeList, $reviewFeedbackInitialEpisodeCodeList))) {
            throw new LogicException('import review feedback episode code not exist', ErrorCode::IMPORT_REVIEW_FEEDBACK_EPISODE_CODE_NOT_EXIST);
        }


        $stepList = [];
        if (!empty($stepName)) {
            $stepList = model(StepModel::class)->where(['name' => ["IN", $stepName]])->select();
            $stepList = array_column($stepList, null, 'name');
        }

        $stepCategoryList = [];
        if (!empty($stepCategoryName)) {
            $stepCategoryList = model(StepCategoryModel::class)->where(['name' => ["IN", $stepCategoryName]])->select();
            $stepCategoryList = array_column($stepCategoryList, null, 'name');
        }

        // 需要过滤 有没有相关的任务
        $assetStepMap = [];
        foreach ($assetData as $assetDataIndex => $assetDataItem) {
            $currentEntity = $entityMap[$assetDataItem['name']];
            $assetDataItem['entity_id'] = $currentEntity['id'];
            $assetDataItem['initial_episode_code'] = $currentEntity['initial_episode_code'];
            $assetDataItem['project_id'] = $projectId;
            $assetDataItem['review_feedback_id'] = $reviewFeedbackId;

            if ($assetDataItem['type'] == 'step_category') {
                if (!isset($assetStepMap[$currentEntity['id']]['step_category'])) {
                    $assetStepMap[$currentEntity['id']]['step_category'][] = $stepCategoryList[$assetDataItem['step_category_name']]['id'];
                } elseif (!in_array($stepCategoryList[$assetDataItem['step_category_name']]['id'], $assetStepMap[$currentEntity['id']]['step_category'])) {
                    $assetStepMap[$currentEntity['id']]['step_category'][] = $stepCategoryList[$assetDataItem['step_category_name']]['id'];
                }

                $assetDataItem['step_category_id'] = $stepCategoryList[$assetDataItem['step_category_name']]['id'];
            } else {
                if (!isset($assetStepMap[$currentEntity['id']]['step'])) {
                    $assetStepMap[$currentEntity['id']]['step'][] = $stepList[$assetDataItem['step_name']]['id'];
                } elseif (!in_array($stepList[$assetDataItem['step']]['id'], $assetStepMap[$currentEntity['id']]['step_category'])) {
                    $assetStepMap[$currentEntity['id']]['step'][] = $stepList[$assetDataItem['step_name']]['id'];
                }

                $assetDataItem['step_id'] = $stepList[$assetDataItem['step_name']]['id'];
            }

            $assetData[$assetDataIndex] = $assetDataItem;
        }


        $relationTaskWhere = [];
        foreach ($assetStepMap as $assetId => $assetStepMapData) {

            $currentStepWhere = [];
            if (isset($assetStepMapData['step'])) {
                $currentStepWhere['task.step_id'] = ["IN", $assetStepMapData['step']];
            }

            if (isset($assetStepMapData['step_category'])) {
                $currentStepWhere['task.step_category_id'] = ["IN", $assetStepMapData['step_category']];
            }
            if (isset($assetStepMapData['step']) && isset($assetStepMapData['step_category'])) {
                $currentStepWhere["_logic"] = "OR";
            }


            $relationTaskWhere[] = [
                'entity.id' => $assetId,
                $currentStepWhere
            ];
        }

        $taskMap = [];
        if (!empty($relationTaskWhere)) {
            $relationTaskWhere["_logic"] = "OR";

            $statusService = StatusService::getInstance();
            $taskDefaultStatus = $statusService->getTaskDefaultStatus(); // 未开始
            $taskReadyStatus = $statusService->getTaskDefaultReadyStatus(); // 就绪
            $taskWaitAssembleStatus = $statusService->getTaskWaitAssembleStatus(); // 待组装
            $taskClosedStatus = $statusService->getTaskDefaultClosedStatus(); // 已取消
            $filterTaskStatus = [$taskDefaultStatus['id'], $taskReadyStatus['id'], $taskWaitAssembleStatus['id'], $taskClosedStatus['id']];


            //需要过滤掉未开始的
            $taskList = model(EntityModel::class)
                ->join("task on entity.id = task.entity_id")
                ->where([
                    'entity.project_id' => $projectId,
                    $relationTaskWhere,
                    'task.step_type' => 'step',
                    "JSON_UNQUOTE(JSON_EXTRACT(task.json,'$.task_status'))" => ["NOT IN", $filterTaskStatus]
                ])
                ->field("task.id,task.entity_id,task.step_id,task.step_category_id")
                ->select();

            //过滤相应的资产工序以及环节
            foreach ($taskList as $taskItem) {
                $currentTaskStepCategoryKey = 'step_category_' . $taskItem['entity_id'] . '_' . $taskItem['step_category_id'];
                $currentTaskStepKey = 'step_' . $taskItem['entity_id'] . '_' . $taskItem['step_id'];
                $taskMap[$currentTaskStepCategoryKey] = $currentTaskStepCategoryKey;
                $taskMap[$currentTaskStepKey] = $taskItem['id'];
            }
        }

        //返修类型限制
        $reasonTypeList = OptionsService::getInstance()->getOptionsConfigItemData("review_feedback_reason_type_config", "type");
        $reasonTypeList = explode(",", $reasonTypeList);

        foreach ($assetData as $assetDataItem) {
            if ($assetDataItem['type'] == 'step_category') {
                $assetDataKey = 'step_category_' . $assetDataItem['entity_id'] . '_' . $assetDataItem['step_category_id'];
                $currentName = join("/", [$assetDataItem['name'], $assetDataItem['step_category_name']]);
            } else {
                $assetDataKey = 'step_' . $assetDataItem['entity_id'] . '_' . $assetDataItem['step_id'];
                $currentName = join("/", [$assetDataItem['name'], $assetDataItem['step_name']]);
            }

            if (!isset($taskMap[$assetDataKey]) || (empty($assetDataItem["text"]) && empty($assetDataItem['media']))) {
                continue;
            }

            if ($assetDataItem['type'] == 'step') {
                $assetDataItem['task_id'] = $taskMap[$assetDataKey];
            }

            if (!isset($assetDataItem["mark_modify"])) {
                $assetDataItem["mark_modify"] = '否';
            }

            $reasonType = $assetDataItem["reason_type"] ?? "";
            if (!in_array($reasonType, $reasonTypeList)) {
                throw new LogicException("{$currentName}下的{$reasonType}返修类型不存在", ErrorCode::IMPORT_FEEDBACK_NOTE_FAILURE);
            }

            $tmp = [
                "note" => [
                    "text" => $assetDataItem["text"] ?? "",
                    "module_code" => "asset",
                    "project_id" => $projectId,
                    "reason_type" => $reasonType,
                    "mark_modify" => $assetDataItem["mark_modify"] == '是' ? "yes" : "no",
                    "review_feedback_id" => $reviewFeedbackId,
                ],
            ];

            if (isset($assetDataItem['media'])) {
                $tmp["note_cos_media"] = $assetDataItem['media'];
            }

            if ($assetDataItem['type'] == 'step') {
                $tmp["note_relation"] = [
                    "type" => 'step',
                    'entity_id' => $assetDataItem['entity_id'],
                    'initial_episode_code' => $assetDataItem['initial_episode_code'],
                    'task_id' => $assetDataItem['task_id']
                ];
            } else {
                $tmp["note_relation"] = [
                    "type" => 'step_category',
                    'entity_id' => $assetDataItem['entity_id'],
                    'initial_episode_code' => $assetDataItem['initial_episode_code'],
                    'step_category_id' => $assetDataItem['step_category_id']
                ];
            }

            $result[] = $tmp;
        }

        return $result;
    }

    /**
     * 导入镜头返修反馈意见
     * @param $shotData
     * @param $projectId
     * @param $reviewFeedback
     * @return array
     */
    public function importShotRepairFeedbackNote($shotData, $projectId, $reviewFeedback)
    {
        $reviewFeedbackId = $reviewFeedback['id'];

        $reviewFeedbackInitialEpisodeCodeList = explode(",", $reviewFeedback['episode_codes']);
        $result = [];

        $shotGroup = [];

        $initialEpisodeCodeList = [];
        foreach ($shotData as $shotDataItem) {
            $initialEpisodeCodeList[$shotDataItem['initial_episode_code']] = $shotDataItem['initial_episode_code'];

            if (!isset($shotGroup[$shotDataItem['initial_episode_code']][$shotDataItem['showings_number']][$shotDataItem['name']])) {
                $shotGroup[$shotDataItem['initial_episode_code']][$shotDataItem['showings_number']][$shotDataItem['name']] = [$shotDataItem["step_category_name"]];
            } elseif (!in_array($shotDataItem["step_category_name"], $shotGroup[$shotDataItem['initial_episode_code']][$shotDataItem['showings_number']][$shotDataItem['name']])) {
                $shotGroup[$shotDataItem['initial_episode_code']][$shotDataItem['showings_number']][$shotDataItem['name']][] = $shotDataItem["step_category_name"];
            }

            $stepCategoryName[$shotDataItem["step_category_name"]] = $shotDataItem["step_category_name"];
        }

        $initialEpisodeCodeList = array_values($initialEpisodeCodeList);

        if (!empty(array_diff($initialEpisodeCodeList, $reviewFeedbackInitialEpisodeCodeList))) {
            throw new LogicException('import review feedback episode code not exist', ErrorCode::IMPORT_REVIEW_FEEDBACK_EPISODE_CODE_NOT_EXIST);
        }

        $stepCategoryList = [];
        if (!empty($stepCategoryName)) {
            $stepCategoryList = model(StepCategoryModel::class)->where(['name' => ["IN", $stepCategoryName]])->select();
            $stepCategoryList = array_column($stepCategoryList, null, 'name');
        }

        $shotGroupWhere = [];
        foreach ($shotGroup as $initialEpisodeCode => $shotGroupItem) {
            foreach ($shotGroupItem as $showingsNumber => $shotNameData) {
                foreach ($shotNameData as $shotName => $stepCategoryNameList) {


                    $stepCategoryIds = [];
                    foreach ($stepCategoryNameList as $stepCategoryNameItem) {
                        if (!isset($stepCategoryList[$stepCategoryNameItem])) {
                            throw new LogicException("{$stepCategoryNameItem} step category not found", ErrorCode::STEP_CATEGORY_NOT_FOUND);
                        }

                        $stepCategoryIds[] = $stepCategoryList[$stepCategoryNameItem]['id'];
                    }

                    $shotGroupWhere[] = [
                        'entity.initial_episode_code' => $initialEpisodeCode,
                        'entity.showings_number' => $showingsNumber,
                        'entity.code' => $shotName,
                        'task.step_category_id' => ["IN", $stepCategoryIds]
                    ];
                }
            }
        }

        // 判定任务是否都被废除
        $statusService = StatusService::getInstance();
        $taskDefaultStatus = $statusService->getTaskDefaultStatus(); // 未开始
        $taskReadyStatus = $statusService->getTaskDefaultReadyStatus(); // 就绪
        $taskWaitAssembleStatus = $statusService->getTaskWaitAssembleStatus(); // 待组装
        $taskClosedStatus = $statusService->getTaskDefaultClosedStatus(); // 已取消
        $filterTaskStatus = join(",", [$taskDefaultStatus['id'], $taskReadyStatus['id'], $taskWaitAssembleStatus['id'], $taskClosedStatus['id']]);

        $shotGroupWhere['_logic'] = "OR";
        $taskList = model(EntityModel::class)
            ->join("task on entity.id = task.entity_id")
            ->where(['entity.project_id' => $projectId, 'entity.module_id' => module_by_code('shot')['id'], $shotGroupWhere, 'task.step_type' => 'step'])
            ->field("entity.initial_episode_code,entity.showings_number,entity.code,entity.id,task.step_category_id,count(task.json->>'$.task_status' not in ({$filterTaskStatus}) or null) as count_no_closed")
            ->group("entity.initial_episode_code,entity.showings_number,entity.code,entity.id,task.step_category_id")
            ->select();

        $taskDataList = [];
        foreach ($taskList as $taskListItem) {
            $taskDataList[join("_", [
                $taskListItem["initial_episode_code"],
                $taskListItem["showings_number"],
                $taskListItem["code"],
                $taskListItem["step_category_id"]
            ])] = $taskListItem;
        }

        $reasonTypeList = OptionsService::getInstance()->getOptionsConfigItemData("review_feedback_reason_type_config", "type");
        $reasonTypeList = explode(",", $reasonTypeList);

        foreach ($shotData as $shotDataItem) {
            $currentTaskData = $taskDataList[join("_", [
                $shotDataItem["initial_episode_code"],
                $shotDataItem["showings_number"],
                $shotDataItem["name"],
                $stepCategoryList[$shotDataItem["step_category_name"]]['id']
            ])] ?? null;

            $shotStepCategoryName = join('/', [$shotDataItem["initial_episode_code"], $shotDataItem["showings_number"], $shotDataItem["name"], $shotDataItem["step_category_name"]]);

            if (!isset($currentTaskData)) {
                throw new LogicException("{$shotStepCategoryName}环节不存在", ErrorCode::IMPORT_FEEDBACK_NOTE_FAILURE);
            }

            if (empty($shotDataItem["text"]) && empty($shotDataItem['media'])) {
                throw new LogicException("{$shotStepCategoryName}环节下的反馈的内容不能为空", ErrorCode::IMPORT_FEEDBACK_NOTE_FAILURE);
            }

            $shotDataItem['step_category_id'] = $stepCategoryList[$shotDataItem["step_category_name"]]['id'];
            if (!isset($shotDataItem["mark_modify"])) {
                $shotDataItem["mark_modify"] = '否';
            }

            //找到被废除的
            if (empty($currentTaskData['count_no_closed'])) {
                throw new LogicException("{$shotStepCategoryName}环节下的任务都处于未开始或者以取消的状态,无法发起返修", ErrorCode::IMPORT_FEEDBACK_NOTE_FAILURE);
            }

            $reasonType = $shotDataItem["reason_type"] ?? "";
            if (!in_array($reasonType, $reasonTypeList)) {
                throw new LogicException("{$shotStepCategoryName}环节下的{$reasonType}返修类型不存在", ErrorCode::IMPORT_FEEDBACK_NOTE_FAILURE);
            }

            $tmp = [
                "note" => [
                    "text" => $shotDataItem["text"] ?? "",
                    "module_code" => "shot",
                    "project_id" => $projectId,
                    "reason_type" => $reasonType,
                    "mark_modify" => $shotDataItem["mark_modify"] == '是' ? "yes" : "no",
                    "review_feedback_id" => $reviewFeedbackId,
                ],
                "note_relation" => [
                    "extra_data" => [
                        "step_category_id" => $shotDataItem["step_category_id"]
                    ],
                    "relation_list" => [
                        [
                            "relation_type" => "entity",
                            "entity_id" => $currentTaskData['id']
                        ]
                    ]
                ],
            ];

            if (isset($shotDataItem['media'])) {
                $tmp["note_cos_media"] = $shotDataItem['media'];
            }

            $result[] = $tmp;
        }

        return $result;
    }


    /**
     * 获取实体-需求  环节外
     * @param array $entityIds 实体ID
     * @param int $reviewFeedbackId 返馈ID
     * @return array
     */
    public function getRelationNoteText(array $entityIds, int $reviewFeedbackId)
    {
        if (empty($entityIds)) {
            return [];
        }
        $relationNoteList = model(NoteEntityRelationModel::class)->alias('rner')
            ->join("note on note.id = rner.note_id")
            ->where([
                'rner.review_feedback_id' => $reviewFeedbackId,
                'rner.relate_module_code' => ['IN', ['shot', 'asset']],
                'rner.relation_type' => 'entity',
                'rner.entity_id' => ['in', $entityIds]
            ])
            ->field("rner.entity_id,note.text, JSON_UNQUOTE(JSON_EXTRACT(note.json, '$.reason_type')) as reason_type")
            ->order("note.id desc")
            ->select();
        return array_group_by($relationNoteList, 'entity_id');

    }


    /**
     * 获取关联的资产实体列表
     * @param        $filter
     * @param string $type
     * @return array
     */
    public function getRelationAssetEntityList($filter, $type = "review_feedback")
    {
        if ($type == "review_feedback") {
            $fields = "relation_note_entity_relation.entity_id as relation_entity_id,note_entity_relation.entity_id,note.demand_status";
        } else {
            $fields = "relation_note_entity_relation.entity_id as relation_entity_id,note_entity_relation.entity_id";
        }

        //查找相关联的 首先找到相关的镜头 关卡和资产
        $relationNoteEntityRelationList = model(RelationNoteEntityRelationModel::class)
            ->join("note_entity_relation on note_entity_relation.id = relation_note_entity_relation.note_entity_relation_id")
            ->join("note on note.id = note_entity_relation.note_id")
            ->where($filter)
            ->field($fields)->select();


        $entityIds = [];
        foreach ($relationNoteEntityRelationList as $relationNoteEntityRelationListItem) {
            $entityIds[$relationNoteEntityRelationListItem["entity_id"]] = $relationNoteEntityRelationListItem["entity_id"];
        }

        if (empty($entityIds)) {
            return [];
        }

        $entityIds = array_values($entityIds);
        $entityList = model(EntityModel::class)
            ->where(["id" => ["IN", $entityIds]])
            ->field("id,name,module_id,JSON_UNQUOTE(JSON_EXTRACT(json, '$.design_category')) AS category")
            ->select();

        //比较出关卡还是资产
        $entityMap = [];
        foreach ($entityList as $entityListItem) {
            if (in_array($entityListItem["category"], ["关卡"])) {
                $entityListItem["module_code"] = module_by_code("level")["code"];
            } else {
                $entityListItem["module_code"] = module_by_code("asset")["code"];
            }
            $entityMap[$entityListItem["id"]] = $entityListItem;
        }

        $relationNoteEntityRelationMap = [];
        //合并资产与关卡的需求状态
        if ($type == "review_feedback") {
            //合并资产与关卡的需求状态
            foreach ($relationNoteEntityRelationList as $relationNoteEntityRelationListItem) {
                $currentRelationEntityId = $relationNoteEntityRelationListItem["relation_entity_id"];
                $currentEntity = $entityMap[$relationNoteEntityRelationListItem["entity_id"]];
                $currentModuleCode = $currentEntity["module_code"];

                if (!isset($relationNoteEntityRelationMap[$currentRelationEntityId][$currentModuleCode][$relationNoteEntityRelationListItem["entity_id"]])) {
                    $relationNoteEntityRelationListItem = array_merge($relationNoteEntityRelationListItem, $currentEntity);
                    $relationNoteEntityRelationMap[$currentRelationEntityId][$currentModuleCode][$relationNoteEntityRelationListItem["entity_id"]] = $relationNoteEntityRelationListItem;
                } else {
                    $relationNoteEntityRelationMap[$currentRelationEntityId][$currentModuleCode][$relationNoteEntityRelationListItem["entity_id"]]["demand_status"] = $this->getPriorityDemandStatus($relationNoteEntityRelationMap[$currentRelationEntityId][$currentModuleCode][$relationNoteEntityRelationListItem["entity_id"]]["demand_status"], $relationNoteEntityRelationListItem["demand_status"]);
                }

                $relationNoteEntityRelationMap[$currentRelationEntityId][$currentModuleCode][$relationNoteEntityRelationListItem["entity_id"]]["accept_changes"] =
                $relationNoteEntityRelationMap[$currentRelationEntityId][$currentModuleCode][$relationNoteEntityRelationListItem["entity_id"]]["repair_progress"] =
                    $relationNoteEntityRelationMap[$currentRelationEntityId][$currentModuleCode][$relationNoteEntityRelationListItem["entity_id"]]["demand_status"];
            }
        } else {
            foreach ($relationNoteEntityRelationList as $relationNoteEntityRelationListItem) {
                $currentRelationEntityId = $relationNoteEntityRelationListItem["relation_entity_id"];
                $currentEntity = $entityMap[$relationNoteEntityRelationListItem["entity_id"]];
                $currentModuleCode = $currentEntity["module_code"];
                $relationNoteEntityRelationMap[$currentRelationEntityId][$currentModuleCode][] = $currentEntity;
            }
        }


        return $relationNoteEntityRelationMap;
    }

    /**
     * 审核反馈的镜头实体列表
     * @param $filter
     * @param $noteFilter
     * @param $isNote
     * @param $reviewFeedbackId
     * @param $page
     * @return array
     */
    public function shotReviewFeedbackEntityList($filter, $noteFilter, $isNote, $reviewFeedbackId, $page, $order)
    {
        $isInitialEpisodeCode = false;
        $this->isExistEpisodeFilter($filter, $isInitialEpisodeCode);

        $order = $order ?? ["entity.initial_episode_code,entity.showings_number,entity.name"];
        if (!empty($filter['asset.name'])) {
            $assetName = $filter['asset.name'];
            unset($filter['asset.name']);
            $shotList = model(NoteEntityRelationModel::class)
                ->join("entity as asset on asset.id = note_entity_relation.entity_id")
                ->join("relation_note_entity_relation on note_entity_relation.id = relation_note_entity_relation.note_entity_relation_id")
                ->join("entity on entity.id = relation_note_entity_relation.entity_id");


            if ($isNote) {
                $shotList = $shotList->join("note on note_entity_relation.note_id = note.id");
            }


            $shotList = $shotList->where([
                'note_entity_relation.review_feedback_id' => $reviewFeedbackId,
                'note_entity_relation.relate_module_code' => 'asset',
                'asset.name' => $assetName
            ]);

            if ($isNote) {
                $shotList = $shotList->where($noteFilter);
            }

            if ($isInitialEpisodeCode) {
                $shotList = $shotList->where($filter);
            }

            $shotList = $shotList
                ->field("entity.id,entity.initial_episode_code,entity.showings_number,entity.name")
                ->group("entity.id")
                ->order($order)
                ->page(...$page)
                ->select();
        } elseif ($isNote) {
            $shotList = model(NoteEntityRelationModel::class)
                ->join("note on note.id = note_entity_relation.note_id")
                ->join("entity on entity.id = note_entity_relation.entity_id")
                ->where([
                    'note_entity_relation.review_feedback_id' => $reviewFeedbackId,
                    'note_entity_relation.relate_module_code' => 'shot',
                ])
                ->where($filter)
                ->field("entity.id,entity.initial_episode_code,entity.showings_number,entity.name")
                ->group("entity.id")
                ->page(...$page)
                ->select();
        } else {
            $reviewFeedback = model(ReviewFeedbackModel::class)->field("project_id,episode_codes")->find($reviewFeedbackId);

            if (empty($reviewFeedback)) {
                return [];
            }

            $statusData = array_column(model(StatusModel::class)->select(), 'id', 'code');
            $statusDataList = [$statusData['closed'], $statusData['wait_for_merge_sequence']];

            $shotList = model(EntityModel::class)
                ->where([
                    "project_id" => $reviewFeedback["project_id"],
                    "module_id" => module_by_code("storyboard")['id'],
                    "initial_episode_code" => ["IN", $reviewFeedback["episode_codes"]],
                    "json_extract(json,'$.storyboard_status')" => ["NOT IN", $statusDataList]
                ])
                ->where($filter)
                ->field("id,initial_episode_code,showings_number,name")
                ->order($order)
                ->page(...$page)
                ->select();
        }

        return $shotList;
    }

    /**
     * 获取note实体关联的数据
     * @param       $noteFilter
     * @param       $reviewFeedbackId
     * @param       $storyboardIds
     * @param       $reviewFeedbackStepCategoryIds
     * @param       $stepCategoryList
     * @param       $moduleCode
     * @param array $stepCategoryMap
     * @return array
     */
    public function getNoteEntityRelationGroupData($noteFilter, $reviewFeedbackId, $storyboardIds, $reviewFeedbackStepCategoryIds, $stepCategoryList, $moduleCode, $stepCategoryMap = [])
    {
        //获取 note_entity_relation 工序分类类型的
        $noteFields = ["note_entity_relation.id", "note_entity_relation.note_id", "note.text", "note.mark_modify",
            "note.demand_status as accept_changes", "note.demand_status as repair_progress", "note.repair_step_id",
            "note.proposer", "JSON_UNQUOTE(JSON_EXTRACT(note.json, '$.note_cos_media')) as cos_media",
            "JSON_UNQUOTE(JSON_EXTRACT(note.json, '$.reason_type')) as reason_type",
            "note_entity_relation.step_category_id", "note_entity_relation.entity_id",
            'note_entity_relation.repair_accept_status', 'note_entity_relation.from_upstream_step_category_id',
        ];

        // 如有分类的筛选,需要去除分类 , 需要查出其他分类的
        $noteEntityRelationFilter = [
            "note.review_feedback_id" => $reviewFeedbackId,
            "note_entity_relation.relation_type" => "step_category",
            "note_entity_relation.relate_module_code" => $moduleCode,
            "entity_id" => ["IN", $storyboardIds],
        ];

        //如果存在分类的筛选,只筛选当前这个分类的,其他分类的不筛选
        if (isset($noteFilter['note_entity_relation.step_category_id'])) {
            $stepCategoryKey = array_search($noteFilter['note_entity_relation.step_category_id'], $reviewFeedbackStepCategoryIds, true);
            unset($reviewFeedbackStepCategoryIds[$stepCategoryKey]);
            $noteEntityRelationFilter[] = [
                "note_entity_relation.step_category_id" => ["IN", $reviewFeedbackStepCategoryIds],
                $noteFilter,
                "_logic" => "OR"
            ];
        } else {
            $noteEntityRelationFilter["note_entity_relation.step_category_id"] = ["IN", $reviewFeedbackStepCategoryIds];
            if (!empty($noteFilter)) {
                $noteEntityRelationFilter[] = $noteFilter;
            }
        }

        $noteEntityRelationList = model(NoteEntityRelationModel::class)
            ->join("note on note.id = note_entity_relation.note_id")
            ->where($noteEntityRelationFilter)
            ->field($noteFields)
            ->order("note.id desc")
            ->select();

        if (empty($noteEntityRelationList)) {
            return [];
        }

        //图片补充
        $cosMedias = [];
        $proposerIds = [];
        foreach ($noteEntityRelationList as $noteEntityRelationIndex => $noteEntityRelationItem) {
            $noteCosMedia = [];
            if (!empty($noteEntityRelationItem["cos_media"])) {
                $noteCosMedia = explode(",", $noteEntityRelationItem["cos_media"]);
            }

            if (!empty($noteEntityRelationItem['proposer'])) {
                $proposerIds[$noteEntityRelationItem['proposer']] = $noteEntityRelationItem['proposer'];
            }

            $cosMedias = array_merge($cosMedias, $noteCosMedia);
            $noteEntityRelationItem["cos_media"] = $noteCosMedia;
            $noteEntityRelationList[$noteEntityRelationIndex] = $noteEntityRelationItem;
        }
        if (!empty($cosMedias)) {
            $cosMediaList = model(CosMediaModel::class)->where(['id' => ["IN", $cosMedias]])->select();
            $cosMediaList = array_column($cosMediaList, null, "id");
        }

        $userMap = [];
        if (!empty($proposerIds)) {
            $userList = model(UserModel::class)->where(['id' => ["IN", $proposerIds]])->field("id,name")->select();
            $userMap = array_column($userList, null, 'id');
        }

        // 以工序分类来分类
        $noteEntityRelationGroup = [];
        foreach ($noteEntityRelationList as $noteEntityRelationItem) {
            $cosMediaTmp = [];
            if (!empty($noteEntityRelationItem["cos_media"])) {
                foreach ($noteEntityRelationItem["cos_media"] as $cosMediaId) {
                    if (isset($cosMediaList[$cosMediaId])) {
                        $cosMediaTmp[] = $cosMediaList[$cosMediaId];
                    }
                }
            }
            $noteEntityRelationItem['from_upstream_step_category_name'] = '';
            if ($noteEntityRelationItem['from_upstream_step_category_id'] && isset($stepCategoryMap[$noteEntityRelationItem["from_upstream_step_category_id"]])) {
                $noteEntityRelationItem['from_upstream_step_category_name'] = $stepCategoryMap[$noteEntityRelationItem["from_upstream_step_category_id"]]['name'];
            }
            $noteEntityRelationItem['proposer'] = $userMap[$noteEntityRelationItem['proposer']] ?? null;
            $noteEntityRelationItem["cos_media"] = $cosMediaTmp;
            $noteEntityRelationGroup[$noteEntityRelationItem["entity_id"]][$stepCategoryList[$noteEntityRelationItem["step_category_id"]]["code"]][] = $noteEntityRelationItem;
        }

        return $noteEntityRelationGroup;
    }

    /**
     * 合并镜头列表
     * @param $shotList
     * @param $noteEntityRelationGroup
     * @param $relationNoteEntityRelationMap
     * @param $defaultStepCategory
     * @param $type
     * @return mixed
     */
    public function mergeShotList(&$shotList, $noteEntityRelationGroup, $relationNoteEntityRelationMap, $defaultStepCategory, $type, $stepCategoryMap, $relationNoteText = [])
    {
        foreach ($shotList as $shotListIndex => $shotListItem) {
            $currentCodeCount = [];
            if (isset($noteEntityRelationGroup[$shotListItem["id"]])) {
                foreach ($noteEntityRelationGroup[$shotListItem["id"]] as $noteEntityRelationGroupStepCategoryCode => $noteEntityRelationGroupItem) {

                    $shotListItem[$noteEntityRelationGroupStepCategoryCode] = array_map(function ($noteEntityRelationGroupItemData) {
                        $noteEntityRelationGroupItemData["upstream_modification"] = "等待算法";
                        return $noteEntityRelationGroupItemData;
                    }, $noteEntityRelationGroupItem);

                    $currentCodeCount[$noteEntityRelationGroupStepCategoryCode] = count($noteEntityRelationGroupItem);
                }
            }
            if (isset($relationNoteEntityRelationMap[$shotListItem["id"]])) {
                foreach ($relationNoteEntityRelationMap[$shotListItem["id"]] as $relationNoteEntityRelationMapStepCategoryCode => $relationNoteEntityRelationMapItem) {
                    $shotListItem[$relationNoteEntityRelationMapStepCategoryCode] = array_values($relationNoteEntityRelationMapItem);
                    $currentCodeCount[$relationNoteEntityRelationMapStepCategoryCode] = count($relationNoteEntityRelationMapItem);
                }
            }

            $maxLengthCode = [];
            foreach ($currentCodeCount as $currentCodeIndex => $currentCodeCountItem) {
                if (empty($maxLengthCode)) {
                    $maxLengthCode = ["code" => $currentCodeIndex, "count" => $currentCodeCountItem];
                } elseif ($maxLengthCode["count"] < $currentCodeCountItem) {
                    $maxLengthCode = ["code" => $currentCodeIndex, "count" => $currentCodeCountItem];
                }
            }
            if (empty($maxLengthCode)) {
                $shotListItem["max_code"] = $defaultStepCategory["code"];
                $shotListItem[$defaultStepCategory["code"]] = [];
            } else {
                $shotListItem["max_code"] = $maxLengthCode["code"];
            }

            //为需求单类型需要添加的字段
            if ($type == "demand_order") {
                $shotJson = json_decode($shotListItem['json'], true);

                $planEndTime = $shotJson['plan_delivery_time'] ?? [];
                unset($shotJson['plan_delivery_time']);

                $entityJson = json_decode($shotListItem['entity_json'], true);

                //补充单个实体上的环节的计划完成时间
                foreach ($planEndTime as $planEndTimeStepCategoryId => $planEndTimeItem) {
                    if (isset($stepCategoryMap[$planEndTimeStepCategoryId])) {
                        $shotListItem[$stepCategoryMap[$planEndTimeStepCategoryId]['prop'] . '_plan_delivery_time'] = intval($planEndTimeItem);
                    }
                }

                unset($shotListItem['json'], $shotListItem['entity_json']);
                $shotListItem = array_merge($shotListItem, $shotJson);
                $shotListItem["design_grade"] = $shotListItem["review_feedback_entity_draft_data"]["design_grade"] ?? $entityJson["design_grade"] ?? "";
                $shotListItem["design_category"] = $shotListItem["review_feedback_entity_draft_data"]["design_category"] ?? $entityJson["design_category"] ?? "";
            }
            //环节外需求描述
            $shotListItem["entity_demand"] = $relationNoteText[$shotListItem["id"]] ?? [];

            $shotList[$shotListIndex] = $shotListItem;
        }

        return $shotList;
    }


    /**
     * 需求单实体列表
     * @param $reviewFeedbackId
     * @param $moduleCode
     * @param $filter
     * @param $page
     * @return array|false|mixed|string
     */
    public function getDemandOrderEntityList($reviewFeedbackId, $moduleCode, $filter, $page, $order)
    {
        $fields = ["entity.id", "review_feedback_entity.initial_episode_code", "review_feedback_entity.showings_number", "entity.name", "entity.json as entity_json", "review_feedback_entity.id as review_feedback_entity_id",
            "review_feedback_entity.is_append", "review_feedback_entity.enable", "review_feedback_entity.published", "review_feedback_entity.json"];

        $moduleId = [module_by_code("shot")['id'], module_by_code("storyboard")['id']];
        if ($moduleCode == "asset") {
            $moduleId = [module_by_code("design")['id'], module_by_code("asset")['id'], module_by_code("level")['id']];
        }

        $order = $order ?? "review_feedback_entity.initial_episode_code asc,review_feedback_entity.showings_number asc,review_feedback_entity.name asc";

        $reviewFeedbackEntityList = model(ReviewFeedbackEntityModel::class)
            ->join("entity on entity.id = review_feedback_entity.entity_id")
            ->where(['review_feedback_entity.review_feedback_id' => $reviewFeedbackId, 'review_feedback_entity.entity_module_id' => ["IN", $moduleId]])
            ->where($filter)
            ->page(...$page)
            ->field($fields)
            ->order($order)
            ->select();

        return $reviewFeedbackEntityList;
    }

    /**
     * 获取所有审核反馈ID
     * @param int $reviewFeedbackId 反馈id
     * @param string $moduleCode 模块code
     * @param array $initialEpisodeCode 初始集
     * @return array
     */
    public function getReviewFeedbackEntityIds(int $reviewFeedbackId, string $moduleCode, array $initialEpisodeCode)
    {
        if (empty($reviewFeedbackId)) {
            return [];
        }
        //查询实体还是--镜头
        $moduleId = [module_by_code("shot")['id'], module_by_code("storyboard")['id']];
        if ($moduleCode == "asset") {
            $moduleId = [module_by_code("design")['id'], module_by_code("asset")['id'], module_by_code("level")['id']];
        }
        //初始集
        $condition = [];
        if ($initialEpisodeCode) {
            $condition['initial_episode_code'] = ['in', $initialEpisodeCode];
        }

        $allIds = model(ReviewFeedbackEntityModel::class)
            ->where([
                'review_feedback_id' => $reviewFeedbackId,
                'entity_module_id' => ["IN", $moduleId],
            ])
            ->where($condition)
            ->field("id")
            ->select();

        $rt = [];
        $rt['review_feedback_entity_ids'] = array_column($allIds, 'id');

        return $rt;
    }

    /**
     * 需求单的头部
     * @param $reviewFeedbackId
     * @param $moduleCode
     * @param $episodeCode
     * @return array
     */
    public function getDemandOrderStepCategoryList($reviewFeedbackId, $moduleCode, $episodeCode)
    {
        $reviewFeedback = model(ReviewFeedbackModel::class)->field("id,project_id,episode_codes,stage_id,json")->find($reviewFeedbackId);
        if (empty($reviewFeedback)) {
            return [];
        }
        if (!empty($reviewFeedback['json'])) {
            $reviewFeedback['json'] = json_decode($reviewFeedback['json'], true);
        }

        $reviewFeedbackEntityModule = "design";
        $assetRepairCount = [
            'level' => 0,
            'asset' => 0,
        ];
        $affectedShotCount = 0;
        if ($moduleCode === "shot") {
            $stepWorkflowCode = ['motion_shot'];
            $reviewFeedbackEntityModule = "storyboard";
            $res = OptionsService::getInstance()->getOptionsData("shot_demand_order_step_category_config");
            $assetRepairList = model(ReviewFeedbackEntityModel::class)
                ->join("relation_note_entity_relation on relation_note_entity_relation.entity_id = review_feedback_entity.entity_id")
                ->join("note_entity_relation on note_entity_relation.id = relation_note_entity_relation.note_entity_relation_id")
                ->join("entity on entity.id = note_entity_relation.entity_id")
                ->where([
                    'review_feedback_entity.review_feedback_id' => $reviewFeedbackId,
                    'review_feedback_entity.entity_module_id' => module_by_code("storyboard")['id'],
                ])
                ->field("entity.id,entity.json")
                ->group("entity.id")
                ->select();
            foreach ($assetRepairList as $assetRepairListItem) {
                $assetJson = json_decode($assetRepairListItem['json'], true);
                if ($assetJson["design_category"] == "关卡") {
                    ++$assetRepairCount['level'];
                } else {
                    ++$assetRepairCount['asset'];
                }
            }
        } else {
            $stepWorkflowCode = ['level', 'design', 'asset'];
            $res = OptionsService::getInstance()->getOptionsData("asset_demand_order_step_category_config");

            //影响镜头个数
            $affectedShotCount = model(ReviewFeedbackEntityModel::class)
                ->join("note_entity_relation on review_feedback_entity.entity_id = note_entity_relation.entity_id and note_entity_relation.review_feedback_id = {$reviewFeedbackId}")
                ->join("relation_note_entity_relation on relation_note_entity_relation.note_entity_relation_id = note_entity_relation.id")
                ->where([
                    'review_feedback_entity.review_feedback_id' => $reviewFeedbackId,
                    'entity_module_id' => module_by_code("design")["id"],
                    'note_entity_relation.episode_code' => $episodeCode,
                ])
                ->count("DISTINCT relation_note_entity_relation.entity_id");
        }

        //需求单下总共的实体个数
        $moduleCount = model(ReviewFeedbackEntityModel::class)
            ->join("entity on entity.id = review_feedback_entity.entity_id")
            ->where([
                'review_feedback_entity.review_feedback_id' => $reviewFeedbackId,
                "review_feedback_entity.entity_module_id" => module_by_code($reviewFeedbackEntityModule)['id'],
                "review_feedback_entity.initial_episode_code" => $episodeCode
            ])
            ->count();

        //需求单下 新增实体个数
        $stepCategoryAppendCount = model(ReviewFeedbackEntityModel::class)
            ->join("note_entity_relation on review_feedback_entity.entity_id = note_entity_relation.entity_id and note_entity_relation.review_feedback_id = {$reviewFeedbackId}")
            ->where([
                'review_feedback_entity.review_feedback_id' => $reviewFeedbackId,
                "review_feedback_entity.entity_module_id" => module_by_code($reviewFeedbackEntityModule)['id'],
                "review_feedback_entity.is_append" => "yes",
                'note_entity_relation.episode_code' => $episodeCode
            ])
            ->field("note_entity_relation.step_category_id,count(DISTINCT review_feedback_entity.entity_id) as count")
            ->group("note_entity_relation.step_category_id")
            ->select();
        $stepCategoryAppendCount = array_column($stepCategoryAppendCount, 'count', 'step_category_id');

        //返修工序
        $stepCategoryRepairCount = model(ReviewFeedbackEntityModel::class)
            ->join("note_entity_relation on review_feedback_entity.entity_id = note_entity_relation.entity_id and note_entity_relation.review_feedback_id = {$reviewFeedbackId}")
            ->join("note on note.id = note_entity_relation.note_id")
            ->where([
                'review_feedback_entity.review_feedback_id' => $reviewFeedbackId,
                "review_feedback_entity.entity_module_id" => module_by_code($reviewFeedbackEntityModule)['id'],
                'note_entity_relation.episode_code' => $episodeCode,
                "note.repair_step_id" => ["GT", 0],
            ])
            ->field("note_entity_relation.step_category_id,count(DISTINCT review_feedback_entity.entity_id) as count")
            ->group("note_entity_relation.step_category_id")
            ->select();

        $stepCategoryRepairCount = array_column($stepCategoryRepairCount, 'count', 'step_category_id');

        $stepCategoryIds = [];
        $stepCategoryNameMap = [];
        $stepCategoryFilter = [];
        $stepCategoryMap = [];
        $repairStepCategoryIds = [];
        foreach ($res as $resItem) {
            if (!empty($resItem['step_category_id'])) {
                $stepCategoryIds[] = $resItem['step_category_id'];
                if (!empty($resItem['children_step_category_id'])) {
                    $repairStepCategoryIds = array_merge($repairStepCategoryIds, $resItem['children_step_category_id']);
                    foreach ($resItem['children_step_category_id'] as $childrenStepCategoryId) {
                        $stepCategoryMap[$childrenStepCategoryId] = [
                            'id' => $childrenStepCategoryId,
                            'name' => $resItem['name'],
                            'code' => $resItem['prop'],
                        ];
                    }
                }
                $stepCategoryFilter[] = ["step_category_ids" => ['FIND_IN_SET', $resItem['step_category_id']]];
                $stepCategoryNameMap[$resItem['step_category_id']] = $resItem['name'];
            }
        }

        if (!empty($stepCategoryFilter)) {
            $stepCategoryFilter["_logic"] = "OR";
            $stepCategoryFilter = [$stepCategoryFilter];
        }

        //未确认的意见统计个数
        $noConfirmNoteStatus = join(",", [sprintf("'%s'", NoteModel::DEMAND_STATUS_DEFAULT)]);

        $noConfirmNoteList = model(NoteEntityRelationModel::class)
            ->join("note on note.id = note_entity_relation.note_id")
            ->where(['note_entity_relation.review_feedback_id' => $reviewFeedback['id'], 'episode_code' => $episodeCode])
            ->field("step_category_id,count(note.id) as count,count(note.demand_status in ({$noConfirmNoteStatus}) or null) as no_confirm_count")
            ->group("step_category_id")
            ->select();

        $noConfirmNoteCount = array_column($noConfirmNoteList, null, 'step_category_id');

        //查询剧集及其封板时间
        $episodeList = model(EntityModel::class)
            ->where(['project_id' => $reviewFeedback['project_id'], 'code' => $episodeCode, 'module_id' => module_by_code("episode")['id']])
            ->field("id,code,json_unquote(json_extract(json, '$.episode_is_seal')) as episode_is_seal")
            ->select();

        $episodeIds = [];
        $noSealEpisodeIds = [];
        $sealEpisodeIds = [];
        $episodeCodeMap = [];
        foreach ($episodeList as $episodeItem) {
            $episodeIds[] = $episodeItem['id'];
            if (!empty($episodeItem['episode_is_seal']) && $episodeItem['episode_is_seal'] == "no") {
                $noSealEpisodeIds[] = $episodeItem['id'];
            } else {
                $sealEpisodeIds[] = $episodeItem['id'];
            }
            $episodeCodeMap[$episodeItem['code']] = $episodeItem;
        }
        //查询集下的环节封板时间
        $episodeStepCategorySeal = [];
        if (!empty($noSealEpisodeIds)) {
            $episodeStepCategorySealTimeList = model(EpisodeStepCategorySealTimeModel::class)->where(['entity_id' => ["IN", $noSealEpisodeIds], $stepCategoryFilter])->select();
            foreach ($episodeStepCategorySealTimeList as $episodeStepCategorySealTimeItem) {
                //判定该时间是否封板
                $isSeal = $episodeStepCategorySealTimeItem['seal_time'] < time();
                if (!$isSeal) {
                    continue;
                }
                $episodeStepCategoryIds = explode(",", $episodeStepCategorySealTimeItem['step_category_ids']);
                foreach ($episodeStepCategoryIds as $episodeStepCategoryId) {
                    $episodeStepCategorySeal[$episodeStepCategorySealTimeItem['entity_id']][$episodeStepCategoryId] = true;
                }
            }
        }


        //统计各个环节有该环节的镜头数,已取消的不统计
        $stepCategoryCount = [];
        if ($moduleCode === "shot") {
            $statusDataList = array_column(model(StatusModel::class)->where(['code' => ["IN", ["closed", "wait_for_merge_sequence"]]])->select(), 'id');

            $stepCategoryCount = model(ReviewFeedbackEntityModel::class)
                ->join("entity on entity.id = review_feedback_entity.entity_id")
                ->join("task on task.entity_id = entity.id")
                ->where([
                    'task.project_id' => $reviewFeedback['project_id'],
                    'task.episode_id' => ["IN", $episodeIds],
                    'task.entity_module_id' => module_by_code('shot')['id'],
                    "task.step_category_id" => ["IN", $stepCategoryIds],
                    "JSON_UNQUOTE(JSON_EXTRACT(task.json, '$.task_status'))" => ["NOT IN", $statusDataList],
                    "JSON_UNQUOTE(JSON_EXTRACT(entity.json, '$.shot_status'))" => ["NOT IN", $statusDataList],
                ])
                ->field("task.step_category_id,COUNT(DISTINCT task.entity_id) as count")
                ->group("task.step_category_id")
                ->select();
            $stepCategoryCount = array_column($stepCategoryCount, 'count', 'step_category_id');
        }


        //拿取stage 的 环节返修比例
        $stage = model(StageModel::class)
            ->field("ratio_list")
            ->find($reviewFeedback['stage_id']);

        $stepCategoryRatioMap = [];
        if (!empty($stage)) {
            $ratioList = json_decode($stage['ratio_list'] ?? "{}", true);
            if (!empty($ratioList)) {
                foreach ($ratioList as $ratioItem) {
                    $stepCategoryRatioMap[$ratioItem['step_category_id']] = [
                        "add_percentage" => intval($ratioItem['add_percentage'] ?? 0),
                        "edit_percentage" => intval($ratioItem['edit_percentage'] ?? 0)
                    ];
                }
            }
        }

        $singlePlanMap = [];
        if (!empty($episodeIds)) {
            $singlePlanList = model(PlanModel::class)
                ->join("task_relation_tag on task_relation_tag.task_id = plan.task_id and task_relation_tag.link_module_code in ('step_category','show_step_category')")
                ->where(['plan.type' => PlanModel::TYPE_STEP_CATEGORY, 'plan.scope' => PlanModel::SCOPE_SINGLE, 'plan.entity_id' => ["IN", $episodeIds], 'task_relation_tag.link_id' => ["IN", $stepCategoryIds]])
                ->field("plan.id,plan.entity_id,task_relation_tag.link_id,plan.start_time,plan.end_time")
                ->group("task_relation_tag.link_id")
                ->select();

            $singlePlanMap = [];
            foreach ($singlePlanList as $singlePlanItem) {
                //判定是否封板
                $singlePlanItem['is_seal'] = "no";
                if (in_array($singlePlanItem['entity_id'], $sealEpisodeIds) || isset($episodeStepCategorySeal[$singlePlanItem['entity_id']][$singlePlanItem['link_id']])) {
                    $singlePlanItem['is_seal'] = "yes";
                }
                $singlePlanMap[$singlePlanItem['link_id']] = $singlePlanItem;
            }
        }

        //返修工序
        $project = model(ProjectModel::class)->field("project_template_id")->find($reviewFeedback['project_id']);
        $stepWorkflowList = model(StepWorkflowModel::class)
            ->where(['project_template_id' => $project["project_template_id"], "code" => ["IN", $stepWorkflowCode]])
            ->field("id,config")
            ->select();

        $stepCode = [];
        foreach ($stepWorkflowList as $stepWorkflowItem) {
            $config = json_decode($stepWorkflowItem['config'], true);
            if (!empty($config["nodes"])) {
                foreach ($config["nodes"] as $nodeArray) {
                    foreach ($nodeArray as $nodeItem) {
                        $stepCode[$nodeItem['module_code']] = $nodeItem['level'];
                    }
                }
            }
        }

        $stepWorkflowMap = [];
        $stepList = [];
        if (!empty($stepCode)) {
            $stepList = StepService::getInstance()->getList([
                'code' => ["IN", array_keys($stepCode)],
                'step_category_id' => ['IN', $repairStepCategoryIds],
            ], "id,name,code,step_category_id");
            foreach ($stepList as $stepIndex => $stepItem) {
                $stepItem['level'] = $stepCode[$stepItem['code']];
                $stepList[$stepIndex] = $stepItem;
            }
            multiple_sort_by($stepList, ['level' => 'asc']);
            $stepWorkflowMap = array_group_by($stepList, "step_category_id");
        }

        // 查询环节上下游关系
        $stepCategoryDownstreamList = [];
        if (!empty($stepList)) {
            $stepUpstreamList = model(StepUpstreamModel::class)
                ->where(["config->>'$.step_id'" => ['IN', array_column($stepList, 'id')]])
                ->field("distinctrow step_id dw_step_id,config->>'$.step_id' uw_step_id")
                ->select();
            // 找出关卡的环节 只有关卡是不能和
            $showSecondLevelStepCategoryList = StepCategoryService::getInstance()->showSecondLevelList();
            $assetStepCategoryIdList = [];
            $levelStepCategoryIdList = [];
            foreach ($showSecondLevelStepCategoryList as $showSecondLevelStepCategory) {
                switch ($showSecondLevelStepCategory['real_module_code']) {
                    case 'level':
                        $levelStepCategoryIdList[] = $showSecondLevelStepCategory['id'];
                        break;
                    case 'asset':
                        $assetStepCategoryIdList[] = $showSecondLevelStepCategory['id'];
                        break;
                }
            }

            $stepStepCategoryRelationMap = array_column($stepList, 'step_category_id', 'id');
            $stepCategoryDownstreamMap = [];
            foreach ($stepUpstreamList as $stepUpstream) {
                $dwStepCategoryId = $stepStepCategoryRelationMap[$stepUpstream['dw_step_id']] ?? 0;
                $uwStepCategoryId = $stepStepCategoryRelationMap[$stepUpstream['uw_step_id']] ?? 0;
                if (empty($dwStepCategoryId) || empty($uwStepCategoryId)
                    || $dwStepCategoryId == $uwStepCategoryId) {
                    continue;
                }
                // 关卡和资产不能同时出现
                if (
                    (in_array($uwStepCategoryId, $levelStepCategoryIdList) && in_array($dwStepCategoryId, $assetStepCategoryIdList))
                    ||
                    (in_array($uwStepCategoryId, $assetStepCategoryIdList) && in_array($dwStepCategoryId, $levelStepCategoryIdList))
                ) {
                    continue;
                }
                if (!isset($stepCategoryDownstreamList[$uwStepCategoryId])) {
                    $stepCategoryDownstreamList[$uwStepCategoryId][] = $stepCategoryMap[$dwStepCategoryId];
                } else if (!in_array($dwStepCategoryId, $stepCategoryDownstreamMap[$uwStepCategoryId])) {
                    $stepCategoryDownstreamList[$uwStepCategoryId][] = $stepCategoryMap[$dwStepCategoryId];
                }
                $stepCategoryDownstreamMap[$uwStepCategoryId][] = $dwStepCategoryId;
            }
        }
        $stepCategoryDownstreamList = $this->mergeStepCategoryDownstream($stepCategoryDownstreamList);

        return [
            $assetRepairCount, $stepCategoryNameMap, $noConfirmNoteCount, $stepCategoryCount, $singlePlanMap,
            $res, $moduleCount, $stepWorkflowMap, $affectedShotCount, $reviewFeedback, $stepCategoryDownstreamList, $episodeCodeMap,
            $stepCategoryRatioMap, $stepCategoryAppendCount, $stepCategoryRepairCount
        ];
    }

    /**
     * 保存审核反馈计划
     * @param $reviewFeedbackId
     * @param $stepCategoryId
     * @param $time
     * @return array|bool
     * @throws \Exception
     */
    public function saveReviewFeedbackPlan($reviewFeedbackId, $episodeId, $stepCategoryId, $time)
    {
        $reviewFeedback = model(ReviewFeedbackModel::class)->field("id,module_code,json")->find($reviewFeedbackId);

        if (empty($reviewFeedback)) {
            return [];
        }

        $json = [];
        if (!empty($reviewFeedback['json'])) {
            $json = json_decode($reviewFeedback['json'], true);
        }

        $childrenStepCategoryIds = $this->getDemandOrderChildrenStepCategoryId($reviewFeedback['module_code'], $stepCategoryId);
        foreach ($childrenStepCategoryIds as $childrenStepCategoryId) {
            $json['plan_delivery_time'][$episodeId][$childrenStepCategoryId] = $time;
        }

        return model(ReviewFeedbackModel::class)->modifyItem(['id' => $reviewFeedbackId, 'json' => $json]);
    }

    /**
     * 查询所有得下游环节
     * @param $stepCategoryId
     * @param $stepCategoryDownstreamList
     * @param $res
     * @param $resMap
     * @param $level
     * @param $maxLevel
     * @return void
     */
    private function findAllDownstream($stepCategoryId, $stepCategoryDownstreamList, &$res, &$resMap, $level = 0, $maxLevel = 100)
    {
        if (empty($stepCategoryDownstreamList[$stepCategoryId])) {
            return;
        }
        foreach ($stepCategoryDownstreamList[$stepCategoryId] as $downstreamStepCategory) {
            if (!empty($resMap[$downstreamStepCategory['id']])) {
                continue;
            }
            $res[] = $downstreamStepCategory;
            $resMap[$downstreamStepCategory['id']] = true;
            $this->findAllDownstream($downstreamStepCategory['id'], $stepCategoryDownstreamList, $res, $resMap, $level + 1, $maxLevel);
        }
    }

    /**
     * 合并所有下游
     * @param array $stepCategoryDownstreamList
     * @return array
     */
    private function mergeStepCategoryDownstream(array $stepCategoryDownstreamList)
    {
        $res = [];
        foreach ($stepCategoryDownstreamList as $stepCategoryId => $downstreamStepCategoryList) {
            $res[$stepCategoryId] = [];
            $resMap = [];
            $this->findAllDownstream($stepCategoryId, $stepCategoryDownstreamList, $res[$stepCategoryId], $resMap, 1, 100);
        }
        return $res;
    }


    /**
     * 保存审核反馈计划
     * @param $reviewFeedbackEntityId
     * @param $stepCategoryId
     * @param $time
     * @return array|bool
     * @throws \Exception
     */
    public function saveReviewFeedbackEntityPlan($reviewFeedbackEntityId, $stepCategoryId, $time)
    {
        $reviewFeedbackEntity = model(ReviewFeedbackEntityModel::class)->field("id,review_feedback_id,json")->find($reviewFeedbackEntityId);
        if (empty($reviewFeedbackEntity)) {
            return [];
        }

        $reviewFeedback = model(ReviewFeedbackModel::class)->field("id,module_code")->find($reviewFeedbackEntity['review_feedback_id']);

        $json = [];
        if (!empty($reviewFeedbackEntity['json'])) {
            $json = json_decode($reviewFeedbackEntity['json'], true);
        }

        $childrenStepCategoryIds = $this->getDemandOrderChildrenStepCategoryId($reviewFeedback['module_code'], $stepCategoryId);
        foreach ($childrenStepCategoryIds as $childrenStepCategoryId) {
            $json['plan_delivery_time'][$childrenStepCategoryId] = $time;
        }


        return model(ReviewFeedbackEntityModel::class)->modifyItem(['id' => $reviewFeedbackEntityId, 'json' => $json]);
    }


    /**
     * 批量更新需求单实体计划---时间
     * @param array $reviewFeedbackEntityIds 反馈IDs
     * @param int $stepCategoryId 环节ID
     * @param int $time 时间
     * @return array
     */
    public function batchSaveReviewFeedbackEntityPlan(array $reviewFeedbackEntityIds, int $stepCategoryId, int $time)
    {
        $reviewFeedbackEntityModel = model(ReviewFeedbackEntityModel::class);
        //查询--反馈实体表列表
        $reviewFeedbackEntityList = $reviewFeedbackEntityModel->field("id,review_feedback_id,json")->where(['id' => ['in', $reviewFeedbackEntityIds]])->select();
        if (empty($reviewFeedbackEntityList)) {
            return [];
        }
        //获取环节--对映的所有环节
        $childrenStepCategoryIds = $this->getDemandOrderChildrenStepCategoryId('', $stepCategoryId);

        //更新交付时间
        $reviewFeedbackEntityModel->startTrans();
        try {
            foreach ($reviewFeedbackEntityList as $val) {
                $json = [];
                if (!empty($val['json'])) {
                    $json = json_decode($val['json'], true);
                }
                //更新交付时间
                foreach ($childrenStepCategoryIds as $childrenStepCategoryId) {
                    $json['plan_delivery_time'][$childrenStepCategoryId] = $time;
                }
                $reviewFeedbackEntityModel->modifyItem(['id' => $val['id'], 'json' => $json]);
            }
            $reviewFeedbackEntityModel->commit();
        } catch (Throwable $e) {
            $reviewFeedbackEntityModel->rollback();
            throw new LogicException('保存失败', ErrorCode::MEDIA_ADD_FAILURE);
        }
        return [];
    }


    /**
     * 获取需求单子级分类id
     * @param $moduleCode
     * @param $stepCategoryId
     * @return array|mixed
     */
    public function getDemandOrderChildrenStepCategoryId($moduleCode, $stepCategoryId = null)
    {
        //返回资产 和  镜头  两种
        $config1 = OptionsService::getInstance()->getOptionsData("shot_demand_order_step_category_config");
        $config2 = OptionsService::getInstance()->getOptionsData("asset_demand_order_step_category_config");
        $config = array_merge($config1, $config2);

        $stepCategoryMap = [];
        foreach ($config as $configItem) {
            if (!empty($configItem['step_category_id']) && !empty($configItem['children_step_category_id'])) {
                $stepCategoryMap[$configItem['step_category_id']] = $configItem['children_step_category_id'];
            }
        }

        if (!empty($stepCategoryId)) {
            return $stepCategoryMap[$stepCategoryId];
        } else {
            return $stepCategoryMap;
        }

    }

    /**
     * 查询任务是否限制添加返修项
     * @param array $taskIdList
     * @return array
     */
    public function restrictionAddRepairFeedbackNote(array $taskIdList = []): array
    {
        if (empty($taskIdList)) {
            return [];
        }
        $statusService = StatusService::getInstance();
        $doneStatus = $statusService->getTaskDoneStatus()['id'];
        $taskList = model(TaskModel::class)
            ->join('step_project on step_project.project_id = task.project_id and step_project.step_id = task.step_id', 'left')
            ->where([
                "task.id" => ["IN", $taskIdList],
                'task.json->>"$.task_status"' => $doneStatus,
            ])
            ->field([
                'task.id',
                'if(is_repair_restricted is not null, is_repair_restricted, "no") as is_repair_restricted'
            ])
            ->select();
        // 项目环节返修限制: 开启时，已完成任务不能添加note
        $res = [];
        foreach ($taskList as $task) {
            if ($task['is_repair_restricted'] == 'yes') {
                $res[] = $task['id'];
            }
        }
        return $res;
    }
}



